/**
* UI Management Module
* Handles DOM updates, table rendering, and metric display
*/
class UIManager {
constructor(calculator) {
this.calculator = calculator;
}
updateSummaryMetrics() {
try {
const enabledResults = Object.values(this.calculator.results);
if (enabledResults.length === 0) {
this.setElementText('net-position-direct', 'CHF 0');
this.setElementText('net-position-loan', 'CHF 0');
this.setElementText('roi-percentage-direct', '0%');
this.setElementText('roi-percentage-loan', '0%');
return;
}
// Separate direct and loan results
const directResults = enabledResults.filter(r => r.investmentModel === 'direct');
const loanResults = enabledResults.filter(r => r.investmentModel === 'loan');
// Calculate averages for direct investment
if (directResults.length > 0) {
const avgNetPositionDirect = directResults.reduce((sum, r) => sum + (r.netPosition || 0), 0) / directResults.length;
const avgROIDirect = directResults.reduce((sum, r) => sum + r.roi, 0) / directResults.length;
this.setElementText('net-position-direct', this.formatCurrency(avgNetPositionDirect));
this.setElementText('roi-percentage-direct', this.formatPercentage(avgROIDirect));
// Update styling for direct metrics
const netPositionDirectElement = document.getElementById('net-position-direct');
if (netPositionDirectElement) {
if (avgNetPositionDirect > 0) {
netPositionDirectElement.className = 'fw-bold text-success';
} else if (avgNetPositionDirect < 0) {
netPositionDirectElement.className = 'fw-bold text-danger';
} else {
netPositionDirectElement.className = 'fw-bold';
}
}
}
// Calculate averages for loan investment
if (loanResults.length > 0) {
const avgNetPositionLoan = loanResults.reduce((sum, r) => sum + (r.netPosition || 0), 0) / loanResults.length;
const avgROILoan = loanResults.reduce((sum, r) => sum + r.roi, 0) / loanResults.length;
this.setElementText('net-position-loan', this.formatCurrency(avgNetPositionLoan));
this.setElementText('roi-percentage-loan', this.formatPercentage(avgROILoan));
// Update styling for loan metrics
const netPositionLoanElement = document.getElementById('net-position-loan');
if (netPositionLoanElement) {
if (avgNetPositionLoan > 0) {
netPositionLoanElement.className = 'fw-bold text-success';
} else if (avgNetPositionLoan < 0) {
netPositionLoanElement.className = 'fw-bold text-danger';
} else {
netPositionLoanElement.className = 'fw-bold';
}
}
}
} catch (error) {
console.error('Error updating summary metrics:', error);
}
}
updateComparisonTable() {
try {
const tbody = document.getElementById('comparison-tbody');
if (!tbody) {
console.error('Comparison table body not found');
return;
}
tbody.innerHTML = '';
// Sort results by scenario first, then by model
const sortedResults = Object.values(this.calculator.results).sort((a, b) => {
const scenarioCompare = a.scenario.localeCompare(b.scenario);
if (scenarioCompare !== 0) return scenarioCompare;
return a.investmentModel.localeCompare(b.investmentModel);
});
sortedResults.forEach(result => {
const isDirect = result.investmentModel === 'direct';
const scenarioColor = this.getScenarioColor(result.scenario);
// Model badge with better styling
const modelBadge = isDirect ?
'Direct' :
'Loan';
// Key features based on model
const keyFeatures = isDirect ?
`Grace period: ${result.effectiveGracePeriod || 6} months
` +
`Performance multiplier: ${result.performanceMultiplier ? result.performanceMultiplier.toFixed(2) : '1.0'}x` :
`Fixed ${this.formatPercentage(this.calculator.getInputValues().loanInterestRate * 100)} rate
` +
'Predictable returns';
const row = tbody.insertRow();
row.innerHTML = `