Browse Source

Task/improve type safety in portfolio performance component (#7265)

Improve type safety
pull/7266/head^2
Kenrick Tandrian 1 week ago
committed by GitHub
parent
commit
6278cf6691
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 1
      apps/client/src/app/components/home-overview/home-overview.html
  2. 22
      apps/client/src/app/components/portfolio-performance/portfolio-performance.component.html
  3. 79
      apps/client/src/app/components/portfolio-performance/portfolio-performance.component.ts

1
apps/client/src/app/components/home-overview/home-overview.html

@ -86,7 +86,6 @@
<div class="col"> <div class="col">
<gf-portfolio-performance <gf-portfolio-performance
class="pb-4" class="pb-4"
[deviceType]="deviceType()"
[errors]="errors()" [errors]="errors()"
[isLoading]="isLoadingPerformance()" [isLoading]="isLoadingPerformance()"
[locale]="user()?.settings?.locale" [locale]="user()?.settings?.locale"

22
apps/client/src/app/components/portfolio-performance/portfolio-performance.component.html

@ -1,7 +1,7 @@
<div class="container p-0"> <div class="container p-0">
<div class="no-gutters row"> <div class="no-gutters row">
<div class="status-container text-muted text-right"> <div class="status-container text-muted text-right">
@if (errors?.length > 0 && !isLoading) { @if (errors()?.length > 0 && !isLoading()) {
<ion-icon <ion-icon
i18n-title i18n-title
name="time-outline" name="time-outline"
@ -10,7 +10,7 @@
/> />
} }
</div> </div>
@if (isLoading) { @if (isLoading()) {
<div class="align-items-center d-flex"> <div class="align-items-center d-flex">
<ngx-skeleton-loader <ngx-skeleton-loader
animation="pulse" animation="pulse"
@ -24,25 +24,25 @@
} }
<div <div
class="display-4 font-weight-bold m-0 text-center value-container" class="display-4 font-weight-bold m-0 text-center value-container"
[hidden]="isLoading" [hidden]="isLoading()"
> >
<span #value id="value"></span> <span #value id="value"></span>
</div> </div>
<div class="currency-container flex-grow-1 px-1"> <div class="currency-container flex-grow-1 px-1">
{{ unit }} {{ unit() }}
</div> </div>
</div> </div>
@if (showDetails) { @if (showDetails()) {
<div class="row"> <div class="row">
<div class="d-flex col justify-content-end"> <div class="d-flex col justify-content-end">
<gf-value <gf-value
[colorizeSign]="true" [colorizeSign]="true"
[isCurrency]="true" [isCurrency]="true"
[locale]="locale" [locale]="locale()"
[value]=" [value]="
isLoading isLoading()
? undefined ? undefined
: performance?.netPerformanceWithCurrencyEffect : performance().netPerformanceWithCurrencyEffect
" "
/> />
</div> </div>
@ -50,11 +50,11 @@
<gf-value <gf-value
[colorizeSign]="true" [colorizeSign]="true"
[isPercent]="true" [isPercent]="true"
[locale]="locale" [locale]="locale()"
[value]=" [value]="
isLoading isLoading()
? undefined ? undefined
: performance?.netPerformancePercentageWithCurrencyEffect : performance().netPerformancePercentageWithCurrencyEffect
" "
/> />
</div> </div>

79
apps/client/src/app/components/portfolio-performance/portfolio-performance.component.ts

@ -13,10 +13,11 @@ import { GfValueComponent } from '@ghostfolio/ui/value';
import { import {
ChangeDetectionStrategy, ChangeDetectionStrategy,
Component, Component,
effect,
ElementRef, ElementRef,
Input, inject,
OnChanges, input,
ViewChild viewChild
} from '@angular/core'; } from '@angular/core';
import { IonIcon } from '@ionic/angular/standalone'; import { IonIcon } from '@ionic/angular/standalone';
import { CountUp } from 'countup.js'; import { CountUp } from 'countup.js';
@ -32,58 +33,68 @@ import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
styleUrls: ['./portfolio-performance.component.scss'], styleUrls: ['./portfolio-performance.component.scss'],
templateUrl: './portfolio-performance.component.html' templateUrl: './portfolio-performance.component.html'
}) })
export class GfPortfolioPerformanceComponent implements OnChanges { export class GfPortfolioPerformanceComponent {
@Input() deviceType: string; public readonly errors = input<ResponseError['errors']>();
@Input() errors: ResponseError['errors']; public readonly isLoading = input<boolean>();
@Input() isLoading: boolean; public readonly locale = input<string>(getLocale());
@Input() locale = getLocale(); public readonly performance = input.required<PortfolioPerformance>();
@Input() performance: PortfolioPerformance; public readonly precision = input.required<number, number>({
@Input() precision: number; transform: (value) => {
@Input() showDetails: boolean; return value >= 0 ? value : 2;
@Input() unit: string; }
});
public readonly showDetails = input<boolean>(false);
public readonly unit = input.required<string>();
@ViewChild('value') value: ElementRef; private readonly value =
viewChild.required<ElementRef<HTMLSpanElement>>('value');
public constructor(private notificationService: NotificationService) { private readonly notificationService = inject(NotificationService);
addIcons({ timeOutline });
}
public ngOnChanges() { public constructor() {
this.precision = this.precision >= 0 ? this.precision : 2; addIcons({ timeOutline });
if (this.isLoading) { effect(() => {
if (this.value?.nativeElement) { if (this.isLoading()) {
this.value.nativeElement.innerHTML = ''; if (this.value().nativeElement) {
this.value().nativeElement.innerHTML = '';
} }
} else { } else {
if (isNumber(this.performance?.currentValueInBaseCurrency)) { if (isNumber(this.performance().currentValueInBaseCurrency)) {
new CountUp('value', this.performance?.currentValueInBaseCurrency, { new CountUp('value', this.performance().currentValueInBaseCurrency, {
decimal: getNumberFormatDecimal(this.locale), decimal: getNumberFormatDecimal(this.locale()),
decimalPlaces: this.precision, decimalPlaces: this.precision(),
duration: 1, duration: 1,
separator: getNumberFormatGroup(this.locale) separator: getNumberFormatGroup(this.locale())
}).start(); }).start();
} else if (this.showDetails === false) { } else if (this.showDetails() === false) {
new CountUp( new CountUp(
'value', 'value',
this.performance?.netPerformancePercentageWithCurrencyEffect * 100, this.performance().netPerformancePercentageWithCurrencyEffect * 100,
{ {
decimal: getNumberFormatDecimal(this.locale), decimal: getNumberFormatDecimal(this.locale()),
decimalPlaces: 2, decimalPlaces: 2,
duration: 1, duration: 1,
separator: getNumberFormatGroup(this.locale) separator: getNumberFormatGroup(this.locale())
} }
).start(); ).start();
} else { } else {
this.value.nativeElement.innerHTML = '*****'; this.value().nativeElement.innerHTML = '*****';
} }
} }
});
}
protected onShowErrors() {
const errors = this.errors();
if (!errors?.length) {
return;
} }
public onShowErrors() { const errorMessageParts: string[] = [];
const errorMessageParts = [];
for (const error of this.errors) { for (const error of errors) {
errorMessageParts.push(`${error.symbol} (${error.dataSource})`); errorMessageParts.push(`${error.symbol} (${error.dataSource})`);
} }

Loading…
Cancel
Save