diff --git a/apps/api/src/app/user/update-user-setting.dto.ts b/apps/api/src/app/user/update-user-setting.dto.ts index 668c1bc66..0bb391a35 100644 --- a/apps/api/src/app/user/update-user-setting.dto.ts +++ b/apps/api/src/app/user/update-user-setting.dto.ts @@ -3,7 +3,6 @@ import type { DateRange, ViewMode } from '@ghostfolio/common/types'; -import { Type } from 'class-transformer'; import { IsBoolean, IsIn, @@ -14,6 +13,10 @@ import { } from 'class-validator'; export class UpdateUserSettingDto { + @IsNumber() + @IsOptional() + annualInterestRate?: number; + @IsOptional() @IsString() baseCurrency?: string; diff --git a/apps/client/src/app/pages/portfolio/fire/fire-page.component.ts b/apps/client/src/app/pages/portfolio/fire/fire-page.component.ts index f1b316df7..51c96a829 100644 --- a/apps/client/src/app/pages/portfolio/fire/fire-page.component.ts +++ b/apps/client/src/app/pages/portfolio/fire/fire-page.component.ts @@ -91,6 +91,24 @@ export class FirePageComponent implements OnDestroy, OnInit { }); } + public onAnnualInterestRateChange(annualInterestRate: number) { + this.dataService + .putUserSetting({ annualInterestRate }) + .pipe(takeUntil(this.unsubscribeSubject)) + .subscribe(() => { + this.userService.remove(); + + this.userService + .get() + .pipe(takeUntil(this.unsubscribeSubject)) + .subscribe((user) => { + this.user = user; + + this.changeDetectorRef.markForCheck(); + }); + }); + } + public onRetirementDateChange(retirementDate: Date) { this.dataService .putUserSetting({ diff --git a/apps/client/src/app/pages/portfolio/fire/fire-page.html b/apps/client/src/app/pages/portfolio/fire/fire-page.html index 9a0196c7f..bbc8cbfc8 100644 --- a/apps/client/src/app/pages/portfolio/fire/fire-page.html +++ b/apps/client/src/app/pages/portfolio/fire/fire-page.html @@ -11,6 +11,7 @@ > per month, based on your total assets of - + and a withdrawal rate of 4%. diff --git a/libs/common/src/lib/interfaces/user-settings.interface.ts b/libs/common/src/lib/interfaces/user-settings.interface.ts index 217d7ba4b..d3864ab64 100644 --- a/libs/common/src/lib/interfaces/user-settings.interface.ts +++ b/libs/common/src/lib/interfaces/user-settings.interface.ts @@ -1,6 +1,7 @@ import { ColorScheme, DateRange, ViewMode } from '@ghostfolio/common/types'; export interface UserSettings { + annualInterestRate?: number; baseCurrency?: string; benchmark?: string; colorScheme?: ColorScheme; diff --git a/libs/ui/src/lib/fire-calculator/fire-calculator.component.html b/libs/ui/src/lib/fire-calculator/fire-calculator.component.html index 31451872b..3982cbeb8 100644 --- a/libs/ui/src/lib/fire-calculator/fire-calculator.component.html +++ b/libs/ui/src/lib/fire-calculator/fire-calculator.component.html @@ -37,11 +37,13 @@ /> diff --git a/libs/ui/src/lib/fire-calculator/fire-calculator.component.ts b/libs/ui/src/lib/fire-calculator/fire-calculator.component.ts index 53a382ae3..c20d1695f 100644 --- a/libs/ui/src/lib/fire-calculator/fire-calculator.component.ts +++ b/libs/ui/src/lib/fire-calculator/fire-calculator.component.ts @@ -39,7 +39,7 @@ import { sub } from 'date-fns'; import { isNumber } from 'lodash'; -import { Subject, takeUntil } from 'rxjs'; +import { debounceTime, Subject, takeUntil } from 'rxjs'; import { FireCalculatorService } from './fire-calculator.service'; @@ -52,6 +52,7 @@ import { FireCalculatorService } from './fire-calculator.service'; export class FireCalculatorComponent implements AfterViewInit, OnChanges, OnDestroy { + @Input() annualInterestRate = 5; @Input() colorScheme: ColorScheme; @Input() currency: string; @Input() deviceType: string; @@ -62,6 +63,7 @@ export class FireCalculatorComponent @Input() retirementDate: Date; @Input() savingsRate = 0; + @Output() annualInterestRateChanged = new EventEmitter(); @Output() projectedTotalAmountChanged = new EventEmitter(); @Output() retirementDateChanged = new EventEmitter(); @Output() savingsRateChanged = new EventEmitter(); @@ -100,7 +102,7 @@ export class FireCalculatorComponent this.calculatorForm.setValue( { - annualInterestRate: 5, + annualInterestRate: this.annualInterestRate, paymentPerPeriod: this.savingsRate, principalInvestmentAmount: 0, projectedTotalAmount: this.projectedTotalAmount, @@ -117,21 +119,27 @@ export class FireCalculatorComponent this.initialize(); }); + this.calculatorForm + .get('annualInterestRate') + .valueChanges.pipe(debounceTime(500), takeUntil(this.unsubscribeSubject)) + .subscribe((annualInterestRate) => { + this.annualInterestRateChanged.emit(annualInterestRate); + }); this.calculatorForm .get('paymentPerPeriod') - .valueChanges.pipe(takeUntil(this.unsubscribeSubject)) + .valueChanges.pipe(debounceTime(500), takeUntil(this.unsubscribeSubject)) .subscribe((savingsRate) => { this.savingsRateChanged.emit(savingsRate); }); this.calculatorForm .get('projectedTotalAmount') - .valueChanges.pipe(takeUntil(this.unsubscribeSubject)) + .valueChanges.pipe(debounceTime(500), takeUntil(this.unsubscribeSubject)) .subscribe((projectedTotalAmount) => { this.projectedTotalAmountChanged.emit(projectedTotalAmount); }); this.calculatorForm .get('retirementDate') - .valueChanges.pipe(takeUntil(this.unsubscribeSubject)) + .valueChanges.pipe(debounceTime(500), takeUntil(this.unsubscribeSubject)) .subscribe((retirementDate) => { this.retirementDateChanged.emit(retirementDate); }); @@ -143,10 +151,11 @@ export class FireCalculatorComponent // Wait for the chartCanvas this.calculatorForm.patchValue( { + annualInterestRate: this.annualInterestRate, paymentPerPeriod: this.getPMT(), principalInvestmentAmount: this.getP(), projectedTotalAmount: - Number(this.getProjectedTotalAmount().toFixed(2)) ?? 0, + Number(this.getProjectedTotalAmount().toFixed(0)) ?? 0, retirementDate: this.getRetirementDate() ?? this.DEFAULT_RETIREMENT_DATE }, @@ -165,14 +174,14 @@ export class FireCalculatorComponent this.calculatorForm .get('projectedTotalAmount') .enable({ emitEvent: false }); - this.calculatorForm.get('retirementDate').enable({ emitEvent: false }); } else { this.calculatorForm.get('paymentPerPeriod').disable({ emitEvent: false }); this.calculatorForm .get('projectedTotalAmount') .disable({ emitEvent: false }); - this.calculatorForm.get('retirementDate').disable({ emitEvent: false }); } + + this.calculatorForm.get('retirementDate').disable({ emitEvent: false }); } public ngOnChanges() { @@ -182,10 +191,11 @@ export class FireCalculatorComponent // Wait for the chartCanvas this.calculatorForm.patchValue( { + annualInterestRate: this.annualInterestRate, principalInvestmentAmount: this.fireWealth, paymentPerPeriod: this.savingsRate ?? 0, projectedTotalAmount: - Number(this.getProjectedTotalAmount().toFixed(2)) ?? 0, + Number(this.getProjectedTotalAmount().toFixed(0)) ?? 0, retirementDate: this.getRetirementDate() ?? this.DEFAULT_RETIREMENT_DATE }, @@ -204,14 +214,14 @@ export class FireCalculatorComponent this.calculatorForm .get('projectedTotalAmount') .enable({ emitEvent: false }); - this.calculatorForm.get('retirementDate').enable({ emitEvent: false }); } else { this.calculatorForm.get('paymentPerPeriod').disable({ emitEvent: false }); this.calculatorForm .get('projectedTotalAmount') .disable({ emitEvent: false }); - this.calculatorForm.get('retirementDate').disable({ emitEvent: false }); } + + this.calculatorForm.get('retirementDate').disable({ emitEvent: false }); } public setMonthAndYear(