Browse Source

Merge 392392f417 into 8c80086da1

pull/5729/merge
Sourav Goyal 2 days ago
committed by GitHub
parent
commit
9fd4f677e0
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 33
      apps/client/src/app/pages/portfolio/fire/fire-page.component.ts
  2. 53
      apps/client/src/app/pages/portfolio/fire/fire-page.html
  3. 31
      libs/ui/src/lib/fire-calculator/fire-calculator.component.ts

33
apps/client/src/app/pages/portfolio/fire/fire-page.component.ts

@ -43,6 +43,10 @@ export class GfFirePageComponent implements OnDestroy, OnInit {
public user: User; public user: User;
public withdrawalRatePerMonth: Big; public withdrawalRatePerMonth: Big;
public withdrawalRatePerYear: Big; public withdrawalRatePerYear: Big;
public projectedWithdrawalRatePerMonth: Big;
public projectedWithdrawalRatePerYear: Big;
public projectedRetirementDate: Date;
public projectedAnnualInterestRate: number;
private unsubscribeSubject = new Subject<void>(); private unsubscribeSubject = new Subject<void>();
@ -211,6 +215,21 @@ export class GfFirePageComponent implements OnDestroy, OnInit {
}); });
} }
public onCalculationComplete(event: {
projectedTotalAmount: number;
retirementDate?: Date;
annualInterestRate: number;
}) {
if (event) {
this.projectedRetirementDate = event.retirementDate;
this.projectedAnnualInterestRate = event.annualInterestRate;
this.calculateProjectedWithdrawalRates(event.projectedTotalAmount);
this.changeDetectorRef.markForCheck();
}
}
public ngOnDestroy() { public ngOnDestroy() {
this.unsubscribeSubject.next(); this.unsubscribeSubject.next();
this.unsubscribeSubject.complete(); this.unsubscribeSubject.complete();
@ -225,4 +244,18 @@ export class GfFirePageComponent implements OnDestroy, OnInit {
this.withdrawalRatePerMonth = this.withdrawalRatePerYear.div(12); this.withdrawalRatePerMonth = this.withdrawalRatePerYear.div(12);
} }
} }
private calculateProjectedWithdrawalRates(projectedTotalAmount: number) {
if (
projectedTotalAmount !== undefined &&
projectedTotalAmount !== null &&
this.user?.settings?.safeWithdrawalRate
) {
this.projectedWithdrawalRatePerYear = new Big(projectedTotalAmount).mul(
this.user.settings.safeWithdrawalRate
);
this.projectedWithdrawalRatePerMonth =
this.projectedWithdrawalRatePerYear.div(12);
}
}
} }

53
apps/client/src/app/pages/portfolio/fire/fire-page.html

@ -28,6 +28,7 @@
[retirementDate]="user?.settings?.retirementDate" [retirementDate]="user?.settings?.retirementDate"
[savingsRate]="user?.settings?.savingsRate" [savingsRate]="user?.settings?.savingsRate"
(annualInterestRateChanged)="onAnnualInterestRateChange($event)" (annualInterestRateChanged)="onAnnualInterestRateChange($event)"
(calculationComplete)="onCalculationComplete($event)"
(projectedTotalAmountChanged)="onProjectedTotalAmountChange($event)" (projectedTotalAmountChanged)="onProjectedTotalAmountChange($event)"
(retirementDateChanged)="onRetirementDateChange($event)" (retirementDateChanged)="onRetirementDateChange($event)"
(savingsRateChanged)="onSavingsRateChange($event)" (savingsRateChanged)="onSavingsRateChange($event)"
@ -131,6 +132,58 @@
[value]="user?.settings?.safeWithdrawalRate" /></span [value]="user?.settings?.safeWithdrawalRate" /></span
>. >.
} }
@if (projectedRetirementDate) {
<div class="mt-2">
<ng-container i18n>By</ng-container>
<ng-container>&nbsp;</ng-container>
<span class="font-weight-bold"
>{{ projectedRetirementDate | date: 'MMMM yyyy' }}</span
>
<ng-container i18n
>, this is projected to increase to</ng-container
>
<ng-container>&nbsp;</ng-container>
<span class="font-weight-bold"
><gf-value
class="d-inline-block"
[isCurrency]="true"
[locale]="user?.settings?.locale"
[unit]="user?.settings?.baseCurrency"
[value]="projectedWithdrawalRatePerYear?.toNumber()"
/>
<ng-container>&nbsp;</ng-container>
<ng-container i18n>per year</ng-container></span
>
<ng-container>&nbsp;</ng-container>
<ng-container i18n>or</ng-container>
<ng-container>&nbsp;</ng-container>
<span class="font-weight-bold"
><gf-value
class="d-inline-block"
[isCurrency]="true"
[locale]="user?.settings?.locale"
[unit]="user?.settings?.baseCurrency"
[value]="projectedWithdrawalRatePerMonth?.toNumber()"
/>
<ng-container>&nbsp;</ng-container>
<ng-container i18n>per month</ng-container></span
>
<ng-container i18n>,</ng-container>
<ng-container>&nbsp;</ng-container>
<ng-container i18n>assuming a</ng-container>
<ng-container>&nbsp;</ng-container>
<span class="font-weight-bold"
><gf-value
class="d-inline-block"
[isPercent]="true"
[locale]="user?.settings?.locale"
[precision]="1"
[value]="projectedAnnualInterestRate / 100" /></span
>
<ng-container>&nbsp;</ng-container>
<ng-container i18n>annual interest rate</ng-container>.
</div>
}
</div> </div>
} }
</div> </div>

31
libs/ui/src/lib/fire-calculator/fire-calculator.component.ts

@ -17,6 +17,7 @@ import {
OnChanges, OnChanges,
OnDestroy, OnDestroy,
Output, Output,
SimpleChanges,
ViewChild ViewChild
} from '@angular/core'; } from '@angular/core';
import { import {
@ -91,6 +92,11 @@ export class GfFireCalculatorComponent implements OnChanges, OnDestroy {
@Output() projectedTotalAmountChanged = new EventEmitter<number>(); @Output() projectedTotalAmountChanged = new EventEmitter<number>();
@Output() retirementDateChanged = new EventEmitter<Date>(); @Output() retirementDateChanged = new EventEmitter<Date>();
@Output() savingsRateChanged = new EventEmitter<number>(); @Output() savingsRateChanged = new EventEmitter<number>();
@Output() calculationComplete = new EventEmitter<{
projectedTotalAmount: number;
retirementDate: Date | undefined;
annualInterestRate: number;
}>();
@ViewChild('chartCanvas') chartCanvas: ElementRef<HTMLCanvasElement>; @ViewChild('chartCanvas') chartCanvas: ElementRef<HTMLCanvasElement>;
@ -157,7 +163,7 @@ export class GfFireCalculatorComponent implements OnChanges, OnDestroy {
}); });
} }
public ngOnChanges() { public ngOnChanges(_changes?: SimpleChanges) {
if (isNumber(this.fireWealth) && this.fireWealth >= 0) { if (isNumber(this.fireWealth) && this.fireWealth >= 0) {
this.calculatorForm.setValue( this.calculatorForm.setValue(
{ {
@ -195,6 +201,9 @@ export class GfFireCalculatorComponent implements OnChanges, OnDestroy {
); );
this.calculatorForm.get('principalInvestmentAmount').disable(); this.calculatorForm.get('principalInvestmentAmount').disable();
// Emit initial calculation payload
this.emitCalculationComplete();
this.changeDetectorRef.markForCheck(); this.changeDetectorRef.markForCheck();
}); });
} }
@ -328,6 +337,9 @@ export class GfFireCalculatorComponent implements OnChanges, OnDestroy {
} }
this.isLoading = false; this.isLoading = false;
// Emit after (re)initialization to keep consumers updated
this.emitCalculationComplete();
} }
private getChartData() { private getChartData() {
@ -345,7 +357,8 @@ export class GfFireCalculatorComponent implements OnChanges, OnDestroy {
// Calculate retirement date // Calculate retirement date
// if we want to retire at month x, we need the projectedTotalAmount at month x-1 // if we want to retire at month x, we need the projectedTotalAmount at month x-1
const lastPeriodDate = sub(this.getRetirementDate(), { months: 1 }); const safeRetirementDate = this.getRetirementDate() ?? this.DEFAULT_RETIREMENT_DATE;
const lastPeriodDate = sub(safeRetirementDate, { months: 1 });
const yearsToRetire = lastPeriodDate.getFullYear() - currentYear; const yearsToRetire = lastPeriodDate.getFullYear() - currentYear;
// Time // Time
@ -476,4 +489,18 @@ export class GfFireCalculatorComponent implements OnChanges, OnDestroy {
}) })
); );
} }
private emitCalculationComplete() {
// Use current form values and calculated helpers
const projectedTotalAmount = Number(this.getProjectedTotalAmount());
const retirementDate = this.getRetirementDate();
const annualInterestRate = this.calculatorForm.get('annualInterestRate')
.value;
this.calculationComplete.emit({
projectedTotalAmount,
retirementDate,
annualInterestRate
});
}
} }

Loading…
Cancel
Save