Skip to content

Commit 125f7ce

Browse files
Merge pull request #161 from briandadams85/feature/lean-fi-impact-toggle
feat(impact): add toggle to switch between FI and Lean FI budget views
2 parents d17eb72 + 9832420 commit 125f7ce

2 files changed

Lines changed: 72 additions & 9 deletions

File tree

src/app/forecasting/output/impact/impact.component.html

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,27 @@ <h3 class="fw-bolder">Expense Impact on FI Date</h3>
33
spending to savings contributions, and you can reduce your FI Date by the amount under <strong>Impact on FI
44
Date</strong>.
55
</p>
6+
<div class="text-center mb-3">
7+
<div class="btn-group" role="group">
8+
<button type="button" class="btn btn-sm" [ngClass]="{'btn-primary': !showLeanFi, 'btn-outline-primary': showLeanFi}" (click)="toggleLeanFi(false)">FI Budget</button>
9+
<button type="button" class="btn btn-sm" [ngClass]="{'btn-primary': showLeanFi, 'btn-outline-primary': !showLeanFi}" (click)="toggleLeanFi(true)">Lean FI Budget</button>
10+
</div>
11+
</div>
612
<div class="table-responsive">
713
<table class="table table-striped">
814
<thead>
915
<tr>
1016
<th>Category</th>
11-
<th>FI Budget</th>
12-
<th>Impact on FI Date</th>
17+
<th>{{ showLeanFi ? 'Lean FI Budget' : 'FI Budget' }}</th>
18+
<th>Impact on {{ showLeanFi ? 'Lean FI' : 'FI' }} Date</th>
1319
</tr>
1420
</thead>
1521
<tbody>
1622
<tr *ngFor="let c of spendingCategoriesWithImpact">
1723
<td>{{ c.category.name }}</td>
18-
<td>{{ c.category.fiBudget | currency:calculateInput.currencyIsoCode }}</td>
19-
<td>
24+
<td *ngIf="!showLeanFi">{{ c.category.fiBudget | currency:calculateInput.currencyIsoCode }}</td>
25+
<td *ngIf="showLeanFi">{{ c.category.leanFiBudget | currency:calculateInput.currencyIsoCode }}</td>
26+
<td *ngIf="!showLeanFi">
2027
<span [ngClass]="{'d-none': c.isFi === true}">
2128
<fa-icon [icon]="['fas', 'plus-circle']"></fa-icon>
2229
</span>
@@ -25,7 +32,19 @@ <h3 class="fw-bolder">Expense Impact on FI Date</h3>
2532
</span>
2633
{{ c.impactDate }}
2734
</td>
35+
<td *ngIf="showLeanFi">
36+
<span *ngIf="c.leanImpactDate">
37+
<span [ngClass]="{'d-none': c.isLeanFi === true}">
38+
<fa-icon [icon]="['fas', 'plus-circle']"></fa-icon>
39+
</span>
40+
<span [ngClass]="{'d-none': c.isLeanFi !== true}">
41+
<fa-icon [icon]="['fas', 'check-circle']"></fa-icon>
42+
</span>
43+
{{ c.leanImpactDate }}
44+
</span>
45+
<span *ngIf="!c.leanImpactDate">-</span>
46+
</td>
2847
</tr>
2948
</tbody>
3049
</table>
31-
</div>
50+
</div>

src/app/forecasting/output/impact/impact.component.ts

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,18 @@ export class ImpactComponent implements OnInit, OnChanges {
1414
@Input() forecast: Forecast;
1515

1616
spendingCategoriesWithImpact;
17+
showLeanFi = false;
18+
19+
toggleLeanFi(showLean: boolean) {
20+
this.showLeanFi = showLean;
21+
if (this.spendingCategoriesWithImpact) {
22+
this.spendingCategoriesWithImpact.sort((a, b) => {
23+
const aValue = showLean ? a.category.leanFiBudget : a.category.fiBudget;
24+
const bValue = showLean ? b.category.leanFiBudget : b.category.fiBudget;
25+
return bValue - aValue;
26+
});
27+
}
28+
}
1729

1830
ngOnInit() {
1931
this.calculateData();
@@ -29,6 +41,7 @@ export class ImpactComponent implements OnInit, OnChanges {
2941
}
3042

3143
const currentFiForecast = this.getFiForecast(this.forecast, this.calculateInput.fiNumber);
44+
const currentLeanFiForecast = this.getFiForecast(this.forecast, this.calculateInput.leanFiNumber);
3245

3346
if (!currentFiForecast) {
3447
return;
@@ -38,21 +51,25 @@ export class ImpactComponent implements OnInit, OnChanges {
3851
return group.categories;
3952
}));
4053
const categoriesWithSpending = categories.filter((category) => {
41-
return category.fiBudget > 0;
54+
return category.fiBudget > 0 || category.leanFiBudget > 0;
4255
}).sort((a, b) => {
4356
return b.fiBudget - a.fiBudget;
4457
});
4558

4659
this.spendingCategoriesWithImpact = categoriesWithSpending.map((category) => {
4760
const isFi = currentFiForecast.date < new Date();
61+
const isLeanFi = currentLeanFiForecast && currentLeanFiForecast.date < new Date();
4862

4963
let impactDate = 'Achieved FI!';
50-
if (isFi) {
64+
let leanImpactDate = null;
5165

66+
if (isFi) {
5267
return {
5368
category,
5469
impactDate,
55-
isFi
70+
isFi,
71+
leanImpactDate: isLeanFi ? 'Achieved Lean FI!' : null,
72+
isLeanFi
5673
};
5774
}
5875

@@ -62,10 +79,26 @@ export class ImpactComponent implements OnInit, OnChanges {
6279

6380
impactDate = this.getImpactDateText(currentFiForecast.date, modifiedFiForecast.date);
6481

82+
// Calculate Lean FI impact
83+
if (category.leanFiBudget > 0 && currentLeanFiForecast) {
84+
if (isLeanFi) {
85+
leanImpactDate = 'Achieved Lean FI!';
86+
} else {
87+
const modifiedLeanCalcInput = this.getModifiedCalculateInputForLeanFi(category.leanFiBudget);
88+
const modifiedLeanForecast = new Forecast(modifiedLeanCalcInput);
89+
const modifiedLeanFiForecast = this.getFiForecast(modifiedLeanForecast, modifiedLeanCalcInput.leanFiNumber);
90+
if (modifiedLeanFiForecast) {
91+
leanImpactDate = this.getImpactDateText(currentLeanFiForecast.date, modifiedLeanFiForecast.date);
92+
}
93+
}
94+
}
95+
6596
return {
6697
category,
6798
impactDate,
68-
isFi
99+
isFi,
100+
leanImpactDate,
101+
isLeanFi
69102
};
70103
});
71104
}
@@ -80,6 +113,17 @@ export class ImpactComponent implements OnInit, OnChanges {
80113
return calcInput;
81114
}
82115

116+
private getModifiedCalculateInputForLeanFi(leanFiSpendingReductionPerMonth: number): CalculateInput {
117+
const calcInput = new CalculateInput();
118+
calcInput.annualExpenses = this.calculateInput.annualExpenses;
119+
calcInput.leanAnnualExpenses = this.calculateInput.leanAnnualExpenses - leanFiSpendingReductionPerMonth * 12;
120+
calcInput.annualSafeWithdrawalRate = this.calculateInput.annualSafeWithdrawalRate;
121+
calcInput.expectedAnnualGrowthRate = this.calculateInput.expectedAnnualGrowthRate;
122+
calcInput.netWorth = this.calculateInput.netWorth;
123+
calcInput.monthlyContribution = this.calculateInput.monthlyContribution + leanFiSpendingReductionPerMonth;
124+
return calcInput;
125+
}
126+
83127
private getFiForecast(forecast: Forecast, fiNumber: number) {
84128
return forecast.monthlyForecasts.find(f => f.netWorth >= fiNumber);
85129
}

0 commit comments

Comments
 (0)