Browse Source

Improve usability

* Add debounce
* Persist annualInterestRate
* Partially disable date picker
pull/1779/head
Thomas 3 years ago
parent
commit
8406952dad
  1. 5
      apps/api/src/app/user/update-user-setting.dto.ts
  2. 18
      apps/client/src/app/pages/portfolio/fire/fire-page.component.ts
  3. 16
      apps/client/src/app/pages/portfolio/fire/fire-page.html
  4. 1
      libs/common/src/lib/interfaces/user-settings.interface.ts
  5. 2
      libs/ui/src/lib/fire-calculator/fire-calculator.component.html
  6. 32
      libs/ui/src/lib/fire-calculator/fire-calculator.component.ts

5
apps/api/src/app/user/update-user-setting.dto.ts

@ -3,7 +3,6 @@ import type {
DateRange, DateRange,
ViewMode ViewMode
} from '@ghostfolio/common/types'; } from '@ghostfolio/common/types';
import { Type } from 'class-transformer';
import { import {
IsBoolean, IsBoolean,
IsIn, IsIn,
@ -14,6 +13,10 @@ import {
} from 'class-validator'; } from 'class-validator';
export class UpdateUserSettingDto { export class UpdateUserSettingDto {
@IsNumber()
@IsOptional()
annualInterestRate?: number;
@IsOptional() @IsOptional()
@IsString() @IsString()
baseCurrency?: string; baseCurrency?: string;

18
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) { public onRetirementDateChange(retirementDate: Date) {
this.dataService this.dataService
.putUserSetting({ .putUserSetting({

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

@ -11,6 +11,7 @@
></gf-premium-indicator> ></gf-premium-indicator>
</h4> </h4>
<gf-fire-calculator <gf-fire-calculator
[annualInterestRate]="user?.settings?.annualInterestRate"
[colorScheme]="user?.settings?.colorScheme" [colorScheme]="user?.settings?.colorScheme"
[currency]="user?.settings?.baseCurrency" [currency]="user?.settings?.baseCurrency"
[deviceType]="deviceType" [deviceType]="deviceType"
@ -20,6 +21,7 @@
[projectedTotalAmount]="user?.settings?.projectedTotalAmount" [projectedTotalAmount]="user?.settings?.projectedTotalAmount"
[retirementDate]="user?.settings?.retirementDate" [retirementDate]="user?.settings?.retirementDate"
[savingsRate]="user?.settings?.savingsRate" [savingsRate]="user?.settings?.savingsRate"
(annualInterestRateChanged)="onAnnualInterestRateChange($event)"
(projectedTotalAmountChanged)="onProjectedTotalAmountChange($event)" (projectedTotalAmountChanged)="onProjectedTotalAmountChange($event)"
(retirementDateChanged)="onRetirementDateChange($event)" (retirementDateChanged)="onRetirementDateChange($event)"
(savingsRateChanged)="onSavingsRateChange($event)" (savingsRateChanged)="onSavingsRateChange($event)"
@ -73,12 +75,14 @@
></gf-value> ></gf-value>
per month</span per month</span
>, based on your total assets of >, based on your total assets of
<gf-value <span class="font-weight-bold"
class="d-inline-block" ><gf-value
[currency]="user?.settings?.baseCurrency" class="d-inline-block"
[locale]="user?.settings?.locale" [currency]="user?.settings?.baseCurrency"
[value]="fireWealth?.toNumber()" [locale]="user?.settings?.locale"
></gf-value> [value]="fireWealth?.toNumber()"
></gf-value
></span>
and a withdrawal rate of 4%. and a withdrawal rate of 4%.
</div> </div>
</div> </div>

1
libs/common/src/lib/interfaces/user-settings.interface.ts

@ -1,6 +1,7 @@
import { ColorScheme, DateRange, ViewMode } from '@ghostfolio/common/types'; import { ColorScheme, DateRange, ViewMode } from '@ghostfolio/common/types';
export interface UserSettings { export interface UserSettings {
annualInterestRate?: number;
baseCurrency?: string; baseCurrency?: string;
benchmark?: string; benchmark?: string;
colorScheme?: ColorScheme; colorScheme?: ColorScheme;

2
libs/ui/src/lib/fire-calculator/fire-calculator.component.html

@ -37,11 +37,13 @@
/> />
<mat-datepicker-toggle <mat-datepicker-toggle
matIconSuffix matIconSuffix
[disabled]="false"
[for]="datepicker" [for]="datepicker"
></mat-datepicker-toggle> ></mat-datepicker-toggle>
<mat-datepicker <mat-datepicker
#datepicker #datepicker
startView="multi-year" startView="multi-year"
[disabled]="false"
(monthSelected)="setMonthAndYear($event, datepicker)" (monthSelected)="setMonthAndYear($event, datepicker)"
> >
</mat-datepicker> </mat-datepicker>

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

@ -39,7 +39,7 @@ import {
sub sub
} from 'date-fns'; } from 'date-fns';
import { isNumber } from 'lodash'; import { isNumber } from 'lodash';
import { Subject, takeUntil } from 'rxjs'; import { debounceTime, Subject, takeUntil } from 'rxjs';
import { FireCalculatorService } from './fire-calculator.service'; import { FireCalculatorService } from './fire-calculator.service';
@ -52,6 +52,7 @@ import { FireCalculatorService } from './fire-calculator.service';
export class FireCalculatorComponent export class FireCalculatorComponent
implements AfterViewInit, OnChanges, OnDestroy implements AfterViewInit, OnChanges, OnDestroy
{ {
@Input() annualInterestRate = 5;
@Input() colorScheme: ColorScheme; @Input() colorScheme: ColorScheme;
@Input() currency: string; @Input() currency: string;
@Input() deviceType: string; @Input() deviceType: string;
@ -62,6 +63,7 @@ export class FireCalculatorComponent
@Input() retirementDate: Date; @Input() retirementDate: Date;
@Input() savingsRate = 0; @Input() savingsRate = 0;
@Output() annualInterestRateChanged = new EventEmitter<number>();
@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>();
@ -100,7 +102,7 @@ export class FireCalculatorComponent
this.calculatorForm.setValue( this.calculatorForm.setValue(
{ {
annualInterestRate: 5, annualInterestRate: this.annualInterestRate,
paymentPerPeriod: this.savingsRate, paymentPerPeriod: this.savingsRate,
principalInvestmentAmount: 0, principalInvestmentAmount: 0,
projectedTotalAmount: this.projectedTotalAmount, projectedTotalAmount: this.projectedTotalAmount,
@ -117,21 +119,27 @@ export class FireCalculatorComponent
this.initialize(); this.initialize();
}); });
this.calculatorForm
.get('annualInterestRate')
.valueChanges.pipe(debounceTime(500), takeUntil(this.unsubscribeSubject))
.subscribe((annualInterestRate) => {
this.annualInterestRateChanged.emit(annualInterestRate);
});
this.calculatorForm this.calculatorForm
.get('paymentPerPeriod') .get('paymentPerPeriod')
.valueChanges.pipe(takeUntil(this.unsubscribeSubject)) .valueChanges.pipe(debounceTime(500), takeUntil(this.unsubscribeSubject))
.subscribe((savingsRate) => { .subscribe((savingsRate) => {
this.savingsRateChanged.emit(savingsRate); this.savingsRateChanged.emit(savingsRate);
}); });
this.calculatorForm this.calculatorForm
.get('projectedTotalAmount') .get('projectedTotalAmount')
.valueChanges.pipe(takeUntil(this.unsubscribeSubject)) .valueChanges.pipe(debounceTime(500), takeUntil(this.unsubscribeSubject))
.subscribe((projectedTotalAmount) => { .subscribe((projectedTotalAmount) => {
this.projectedTotalAmountChanged.emit(projectedTotalAmount); this.projectedTotalAmountChanged.emit(projectedTotalAmount);
}); });
this.calculatorForm this.calculatorForm
.get('retirementDate') .get('retirementDate')
.valueChanges.pipe(takeUntil(this.unsubscribeSubject)) .valueChanges.pipe(debounceTime(500), takeUntil(this.unsubscribeSubject))
.subscribe((retirementDate) => { .subscribe((retirementDate) => {
this.retirementDateChanged.emit(retirementDate); this.retirementDateChanged.emit(retirementDate);
}); });
@ -143,10 +151,11 @@ export class FireCalculatorComponent
// Wait for the chartCanvas // Wait for the chartCanvas
this.calculatorForm.patchValue( this.calculatorForm.patchValue(
{ {
annualInterestRate: this.annualInterestRate,
paymentPerPeriod: this.getPMT(), paymentPerPeriod: this.getPMT(),
principalInvestmentAmount: this.getP(), principalInvestmentAmount: this.getP(),
projectedTotalAmount: projectedTotalAmount:
Number(this.getProjectedTotalAmount().toFixed(2)) ?? 0, Number(this.getProjectedTotalAmount().toFixed(0)) ?? 0,
retirementDate: retirementDate:
this.getRetirementDate() ?? this.DEFAULT_RETIREMENT_DATE this.getRetirementDate() ?? this.DEFAULT_RETIREMENT_DATE
}, },
@ -165,14 +174,14 @@ export class FireCalculatorComponent
this.calculatorForm this.calculatorForm
.get('projectedTotalAmount') .get('projectedTotalAmount')
.enable({ emitEvent: false }); .enable({ emitEvent: false });
this.calculatorForm.get('retirementDate').enable({ emitEvent: false });
} else { } else {
this.calculatorForm.get('paymentPerPeriod').disable({ emitEvent: false }); this.calculatorForm.get('paymentPerPeriod').disable({ emitEvent: false });
this.calculatorForm this.calculatorForm
.get('projectedTotalAmount') .get('projectedTotalAmount')
.disable({ emitEvent: false }); .disable({ emitEvent: false });
this.calculatorForm.get('retirementDate').disable({ emitEvent: false });
} }
this.calculatorForm.get('retirementDate').disable({ emitEvent: false });
} }
public ngOnChanges() { public ngOnChanges() {
@ -182,10 +191,11 @@ export class FireCalculatorComponent
// Wait for the chartCanvas // Wait for the chartCanvas
this.calculatorForm.patchValue( this.calculatorForm.patchValue(
{ {
annualInterestRate: this.annualInterestRate,
principalInvestmentAmount: this.fireWealth, principalInvestmentAmount: this.fireWealth,
paymentPerPeriod: this.savingsRate ?? 0, paymentPerPeriod: this.savingsRate ?? 0,
projectedTotalAmount: projectedTotalAmount:
Number(this.getProjectedTotalAmount().toFixed(2)) ?? 0, Number(this.getProjectedTotalAmount().toFixed(0)) ?? 0,
retirementDate: retirementDate:
this.getRetirementDate() ?? this.DEFAULT_RETIREMENT_DATE this.getRetirementDate() ?? this.DEFAULT_RETIREMENT_DATE
}, },
@ -204,14 +214,14 @@ export class FireCalculatorComponent
this.calculatorForm this.calculatorForm
.get('projectedTotalAmount') .get('projectedTotalAmount')
.enable({ emitEvent: false }); .enable({ emitEvent: false });
this.calculatorForm.get('retirementDate').enable({ emitEvent: false });
} else { } else {
this.calculatorForm.get('paymentPerPeriod').disable({ emitEvent: false }); this.calculatorForm.get('paymentPerPeriod').disable({ emitEvent: false });
this.calculatorForm this.calculatorForm
.get('projectedTotalAmount') .get('projectedTotalAmount')
.disable({ emitEvent: false }); .disable({ emitEvent: false });
this.calculatorForm.get('retirementDate').disable({ emitEvent: false });
} }
this.calculatorForm.get('retirementDate').disable({ emitEvent: false });
} }
public setMonthAndYear( public setMonthAndYear(

Loading…
Cancel
Save