Browse Source

Task/improve type safety in home overview component (#6927)

* feat(client): implement default date range

* feat(client): resolve errors

* feat(client): replace constructor based DI with inject function

* feat(client): enforce encapsulation

* fix(client): remove dead code

* feat(client): enforce immutability

* feat(client): replace deprecated getDeviceInfo

* feat(client): convert errors to signal

* feat(client): convert hasImpersonationId to signal

* feat(client): convert user to signal

* feat(client): convert to computed signals

* feat(client): convert historicalDataItems and isLoadingPerformance to signals

* feat(client): convert performance and precision to signals

* feat(client): implement OnPush change detection strategy

* fix(common): revert DateRange type change

* fix(client): format file
pull/6932/head
Kenrick Tandrian 2 days ago
committed by GitHub
parent
commit
081ce27d78
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 7
      apps/api/src/app/endpoints/benchmarks/benchmarks.controller.ts
  2. 11
      apps/api/src/app/portfolio/portfolio.controller.ts
  3. 5
      apps/api/src/app/portfolio/portfolio.service.ts
  4. 4
      apps/api/src/app/user/user.service.ts
  5. 3
      apps/client/src/app/components/account-detail-dialog/account-detail-dialog.component.ts
  6. 151
      apps/client/src/app/components/home-overview/home-overview.component.ts
  7. 42
      apps/client/src/app/components/home-overview/home-overview.html
  8. 4
      apps/client/src/app/components/portfolio-performance/portfolio-performance.component.html
  9. 2
      apps/client/src/app/components/portfolio-performance/portfolio-performance.component.ts
  10. 3
      apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts
  11. 3
      libs/common/src/lib/config.ts

7
apps/api/src/app/endpoints/benchmarks/benchmarks.controller.ts

@ -5,7 +5,10 @@ import { TransformDataSourceInResponseInterceptor } from '@ghostfolio/api/interc
import { ApiService } from '@ghostfolio/api/services/api/api.service'; import { ApiService } from '@ghostfolio/api/services/api/api.service';
import { BenchmarkService } from '@ghostfolio/api/services/benchmark/benchmark.service'; import { BenchmarkService } from '@ghostfolio/api/services/benchmark/benchmark.service';
import { getIntervalFromDateRange } from '@ghostfolio/common/calculation-helper'; import { getIntervalFromDateRange } from '@ghostfolio/common/calculation-helper';
import { HEADER_KEY_IMPERSONATION } from '@ghostfolio/common/config'; import {
DEFAULT_DATE_RANGE,
HEADER_KEY_IMPERSONATION
} from '@ghostfolio/common/config';
import type { import type {
AssetProfileIdentifier, AssetProfileIdentifier,
BenchmarkMarketDataDetailsResponse, BenchmarkMarketDataDetailsResponse,
@ -118,7 +121,7 @@ export class BenchmarksController {
@Param('dataSource') dataSource: DataSource, @Param('dataSource') dataSource: DataSource,
@Param('startDateString') startDateString: string, @Param('startDateString') startDateString: string,
@Param('symbol') symbol: string, @Param('symbol') symbol: string,
@Query('range') dateRange: DateRange = 'max', @Query('range') dateRange: DateRange = DEFAULT_DATE_RANGE,
@Query('accounts') filterByAccounts?: string, @Query('accounts') filterByAccounts?: string,
@Query('assetClasses') filterByAssetClasses?: string, @Query('assetClasses') filterByAssetClasses?: string,
@Query('dataSource') filterByDataSource?: string, @Query('dataSource') filterByDataSource?: string,

11
apps/api/src/app/portfolio/portfolio.controller.ts

@ -14,6 +14,7 @@ import { ConfigurationService } from '@ghostfolio/api/services/configuration/con
import { ImpersonationService } from '@ghostfolio/api/services/impersonation/impersonation.service'; import { ImpersonationService } from '@ghostfolio/api/services/impersonation/impersonation.service';
import { getIntervalFromDateRange } from '@ghostfolio/common/calculation-helper'; import { getIntervalFromDateRange } from '@ghostfolio/common/calculation-helper';
import { import {
DEFAULT_DATE_RANGE,
HEADER_KEY_IMPERSONATION, HEADER_KEY_IMPERSONATION,
UNKNOWN_KEY UNKNOWN_KEY
} from '@ghostfolio/common/config'; } from '@ghostfolio/common/config';
@ -82,7 +83,7 @@ export class PortfolioController {
@Query('accounts') filterByAccounts?: string, @Query('accounts') filterByAccounts?: string,
@Query('assetClasses') filterByAssetClasses?: string, @Query('assetClasses') filterByAssetClasses?: string,
@Query('dataSource') filterByDataSource?: string, @Query('dataSource') filterByDataSource?: string,
@Query('range') dateRange: DateRange = 'max', @Query('range') dateRange: DateRange = DEFAULT_DATE_RANGE,
@Query('symbol') filterBySymbol?: string, @Query('symbol') filterBySymbol?: string,
@Query('tags') filterByTags?: string, @Query('tags') filterByTags?: string,
@Query('withMarkets') withMarketsParam = 'false' @Query('withMarkets') withMarketsParam = 'false'
@ -321,7 +322,7 @@ export class PortfolioController {
@Query('assetClasses') filterByAssetClasses?: string, @Query('assetClasses') filterByAssetClasses?: string,
@Query('dataSource') filterByDataSource?: string, @Query('dataSource') filterByDataSource?: string,
@Query('groupBy') groupBy?: GroupBy, @Query('groupBy') groupBy?: GroupBy,
@Query('range') dateRange: DateRange = 'max', @Query('range') dateRange: DateRange = DEFAULT_DATE_RANGE,
@Query('symbol') filterBySymbol?: string, @Query('symbol') filterBySymbol?: string,
@Query('tags') filterByTags?: string @Query('tags') filterByTags?: string
): Promise<PortfolioDividendsResponse> { ): Promise<PortfolioDividendsResponse> {
@ -422,7 +423,7 @@ export class PortfolioController {
@Query('dataSource') filterByDataSource?: string, @Query('dataSource') filterByDataSource?: string,
@Query('holdingType') filterByHoldingType?: string, @Query('holdingType') filterByHoldingType?: string,
@Query('query') filterBySearchQuery?: string, @Query('query') filterBySearchQuery?: string,
@Query('range') dateRange: DateRange = 'max', @Query('range') dateRange: DateRange = DEFAULT_DATE_RANGE,
@Query('symbol') filterBySymbol?: string, @Query('symbol') filterBySymbol?: string,
@Query('tags') filterByTags?: string @Query('tags') filterByTags?: string
): Promise<PortfolioHoldingsResponse> { ): Promise<PortfolioHoldingsResponse> {
@ -455,7 +456,7 @@ export class PortfolioController {
@Query('assetClasses') filterByAssetClasses?: string, @Query('assetClasses') filterByAssetClasses?: string,
@Query('dataSource') filterByDataSource?: string, @Query('dataSource') filterByDataSource?: string,
@Query('groupBy') groupBy?: GroupBy, @Query('groupBy') groupBy?: GroupBy,
@Query('range') dateRange: DateRange = 'max', @Query('range') dateRange: DateRange = DEFAULT_DATE_RANGE,
@Query('symbol') filterBySymbol?: string, @Query('symbol') filterBySymbol?: string,
@Query('tags') filterByTags?: string @Query('tags') filterByTags?: string
): Promise<PortfolioInvestmentsResponse> { ): Promise<PortfolioInvestmentsResponse> {
@ -527,7 +528,7 @@ export class PortfolioController {
@Query('accounts') filterByAccounts?: string, @Query('accounts') filterByAccounts?: string,
@Query('assetClasses') filterByAssetClasses?: string, @Query('assetClasses') filterByAssetClasses?: string,
@Query('dataSource') filterByDataSource?: string, @Query('dataSource') filterByDataSource?: string,
@Query('range') dateRange: DateRange = 'max', @Query('range') dateRange: DateRange = DEFAULT_DATE_RANGE,
@Query('symbol') filterBySymbol?: string, @Query('symbol') filterBySymbol?: string,
@Query('tags') filterByTags?: string, @Query('tags') filterByTags?: string,
@Query('withExcludedAccounts') withExcludedAccountsParam = 'false' @Query('withExcludedAccounts') withExcludedAccountsParam = 'false'

5
apps/api/src/app/portfolio/portfolio.service.ts

@ -32,6 +32,7 @@ import {
} from '@ghostfolio/common/calculation-helper'; } from '@ghostfolio/common/calculation-helper';
import { import {
DEFAULT_CURRENCY, DEFAULT_CURRENCY,
DEFAULT_DATE_RANGE,
TAG_ID_EMERGENCY_FUND, TAG_ID_EMERGENCY_FUND,
TAG_ID_EXCLUDE_FROM_ANALYSIS, TAG_ID_EXCLUDE_FROM_ANALYSIS,
UNKNOWN_KEY UNKNOWN_KEY
@ -470,7 +471,7 @@ export class PortfolioService {
} }
public async getDetails({ public async getDetails({
dateRange = 'max', dateRange = DEFAULT_DATE_RANGE,
filters, filters,
impersonationId, impersonationId,
userId, userId,
@ -1013,7 +1014,7 @@ export class PortfolioService {
} }
public async getPerformance({ public async getPerformance({
dateRange = 'max', dateRange = DEFAULT_DATE_RANGE,
filters, filters,
impersonationId, impersonationId,
userId userId

4
apps/api/src/app/user/user.service.ts

@ -26,6 +26,7 @@ import { PropertyService } from '@ghostfolio/api/services/property/property.serv
import { TagService } from '@ghostfolio/api/services/tag/tag.service'; import { TagService } from '@ghostfolio/api/services/tag/tag.service';
import { import {
DEFAULT_CURRENCY, DEFAULT_CURRENCY,
DEFAULT_DATE_RANGE,
DEFAULT_LANGUAGE_CODE, DEFAULT_LANGUAGE_CODE,
PROPERTY_IS_READ_ONLY_MODE, PROPERTY_IS_READ_ONLY_MODE,
PROPERTY_SYSTEM_MESSAGE, PROPERTY_SYSTEM_MESSAGE,
@ -281,7 +282,8 @@ export class UserService {
(user.settings.settings as UserSettings).dateRange = (user.settings.settings as UserSettings).dateRange =
(user.settings.settings as UserSettings).viewMode === 'ZEN' (user.settings.settings as UserSettings).viewMode === 'ZEN'
? 'max' ? 'max'
: ((user.settings.settings as UserSettings)?.dateRange ?? 'max'); : ((user.settings.settings as UserSettings)?.dateRange ??
DEFAULT_DATE_RANGE);
// Set default value for performance calculation type // Set default value for performance calculation type
if (!(user.settings.settings as UserSettings)?.performanceCalculationType) { if (!(user.settings.settings as UserSettings)?.performanceCalculationType) {

3
apps/client/src/app/components/account-detail-dialog/account-detail-dialog.component.ts

@ -1,6 +1,7 @@
import { GfInvestmentChartComponent } from '@ghostfolio/client/components/investment-chart/investment-chart.component'; import { GfInvestmentChartComponent } from '@ghostfolio/client/components/investment-chart/investment-chart.component';
import { UserService } from '@ghostfolio/client/services/user/user.service'; import { UserService } from '@ghostfolio/client/services/user/user.service';
import { import {
DEFAULT_DATE_RANGE,
DEFAULT_PAGE_SIZE, DEFAULT_PAGE_SIZE,
NUMERICAL_PRECISION_THRESHOLD_6_FIGURES NUMERICAL_PRECISION_THRESHOLD_6_FIGURES
} from '@ghostfolio/common/config'; } from '@ghostfolio/common/config';
@ -330,7 +331,7 @@ export class GfAccountDetailDialogComponent implements OnInit {
type: 'ACCOUNT' type: 'ACCOUNT'
} }
], ],
range: 'max', range: DEFAULT_DATE_RANGE,
withExcludedAccounts: true, withExcludedAccounts: true,
withItems: true withItems: true
}) })

151
apps/client/src/app/components/home-overview/home-overview.component.ts

@ -2,7 +2,11 @@ import { GfPortfolioPerformanceComponent } from '@ghostfolio/client/components/p
import { LayoutService } from '@ghostfolio/client/core/layout.service'; import { LayoutService } from '@ghostfolio/client/core/layout.service';
import { ImpersonationStorageService } from '@ghostfolio/client/services/impersonation-storage.service'; import { ImpersonationStorageService } from '@ghostfolio/client/services/impersonation-storage.service';
import { UserService } from '@ghostfolio/client/services/user/user.service'; import { UserService } from '@ghostfolio/client/services/user/user.service';
import { NUMERICAL_PRECISION_THRESHOLD_6_FIGURES } from '@ghostfolio/common/config'; import {
DEFAULT_CURRENCY,
DEFAULT_DATE_RANGE,
NUMERICAL_PRECISION_THRESHOLD_6_FIGURES
} from '@ghostfolio/common/config';
import { import {
AssetProfileIdentifier, AssetProfileIdentifier,
LineChartItem, LineChartItem,
@ -15,11 +19,13 @@ import { GfLineChartComponent } from '@ghostfolio/ui/line-chart';
import { DataService } from '@ghostfolio/ui/services'; import { DataService } from '@ghostfolio/ui/services';
import { import {
ChangeDetectorRef, ChangeDetectionStrategy,
Component, Component,
CUSTOM_ELEMENTS_SCHEMA, computed,
DestroyRef, DestroyRef,
OnInit inject,
OnInit,
signal
} from '@angular/core'; } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
@ -27,79 +33,80 @@ import { RouterModule } from '@angular/router';
import { DeviceDetectorService } from 'ngx-device-detector'; import { DeviceDetectorService } from 'ngx-device-detector';
@Component({ @Component({
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [ imports: [
GfLineChartComponent, GfLineChartComponent,
GfPortfolioPerformanceComponent, GfPortfolioPerformanceComponent,
MatButtonModule, MatButtonModule,
RouterModule RouterModule
], ],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
selector: 'gf-home-overview', selector: 'gf-home-overview',
styleUrls: ['./home-overview.scss'], styleUrls: ['./home-overview.scss'],
templateUrl: './home-overview.html' templateUrl: './home-overview.html'
}) })
export class GfHomeOverviewComponent implements OnInit { export class GfHomeOverviewComponent implements OnInit {
public deviceType: string; protected readonly errors = signal<AssetProfileIdentifier[]>([]);
public errors: AssetProfileIdentifier[]; protected readonly hasImpersonationId = signal(false);
public hasError: boolean; protected readonly historicalDataItems = signal<LineChartItem[] | null>(null);
public hasImpersonationId: boolean; protected readonly isLoadingPerformance = signal(true);
public hasPermissionToCreateActivity: boolean; protected readonly performance = signal<PortfolioPerformance | null>(null);
public historicalDataItems: LineChartItem[]; protected readonly performanceLabel = $localize`Performance`;
public isAllTimeHigh: boolean; protected readonly precision = signal(2);
public isAllTimeLow: boolean; protected readonly user = signal<User | null>(null);
public isLoadingPerformance = true;
public performance: PortfolioPerformance; protected readonly routerLinkAccounts = internalRoutes.accounts.routerLink;
public performanceLabel = $localize`Performance`; protected readonly routerLinkPortfolio = internalRoutes.portfolio.routerLink;
public precision = 2; protected readonly routerLinkPortfolioActivities =
public routerLinkAccounts = internalRoutes.accounts.routerLink;
public routerLinkPortfolio = internalRoutes.portfolio.routerLink;
public routerLinkPortfolioActivities =
internalRoutes.portfolio.subRoutes.activities.routerLink; internalRoutes.portfolio.subRoutes.activities.routerLink;
public showDetails = false;
public unit: string; protected readonly deviceType = computed(
public user: User; () => this.deviceDetectorService.deviceInfo().deviceType
);
public constructor(
private changeDetectorRef: ChangeDetectorRef, protected readonly hasPermissionToCreateActivity = computed(() => {
private dataService: DataService, return hasPermission(this.user()?.permissions, permissions.createActivity);
private destroyRef: DestroyRef, });
private deviceDetectorService: DeviceDetectorService,
private impersonationStorageService: ImpersonationStorageService, protected readonly showDetails = computed(() => {
private layoutService: LayoutService, const user = this.user();
private userService: UserService
) { return user
? !user.settings.isRestrictedView && user.settings.viewMode !== 'ZEN'
: false;
});
protected readonly unit = computed(() => {
return this.showDetails()
? (this.user()?.settings?.baseCurrency ?? DEFAULT_CURRENCY)
: '%';
});
private readonly dataService = inject(DataService);
private readonly destroyRef = inject(DestroyRef);
private readonly deviceDetectorService = inject(DeviceDetectorService);
private readonly impersonationStorageService = inject(
ImpersonationStorageService
);
private readonly layoutService = inject(LayoutService);
private readonly userService = inject(UserService);
public constructor() {
this.userService.stateChanged this.userService.stateChanged
.pipe(takeUntilDestroyed(this.destroyRef)) .pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((state) => { .subscribe((state) => {
if (state?.user) { if (state?.user) {
this.user = state.user; this.user.set(state.user);
this.hasPermissionToCreateActivity = hasPermission(
this.user.permissions,
permissions.createActivity
);
this.update(); this.update();
} }
}); });
} }
public ngOnInit() { public ngOnInit() {
this.deviceType = this.deviceDetectorService.getDeviceInfo().deviceType;
this.showDetails =
!this.user.settings.isRestrictedView &&
this.user.settings.viewMode !== 'ZEN';
this.unit = this.showDetails ? this.user.settings.baseCurrency : '%';
this.impersonationStorageService this.impersonationStorageService
.onChangeHasImpersonation() .onChangeHasImpersonation()
.pipe(takeUntilDestroyed(this.destroyRef)) .pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((impersonationId) => { .subscribe((impersonationId) => {
this.hasImpersonationId = !!impersonationId; this.hasImpersonationId.set(!!impersonationId);
this.changeDetectorRef.markForCheck();
}); });
this.layoutService.shouldReloadContent$ this.layoutService.shouldReloadContent$
@ -110,40 +117,40 @@ export class GfHomeOverviewComponent implements OnInit {
} }
private update() { private update() {
this.historicalDataItems = null; this.historicalDataItems.set(null);
this.isLoadingPerformance = true; this.isLoadingPerformance.set(true);
this.dataService this.dataService
.fetchPortfolioPerformance({ .fetchPortfolioPerformance({
range: this.user?.settings?.dateRange range: this.user()?.settings?.dateRange ?? DEFAULT_DATE_RANGE
}) })
.pipe(takeUntilDestroyed(this.destroyRef)) .pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(({ chart, errors, performance }) => { .subscribe(({ chart, errors, performance }) => {
this.errors = errors; this.errors.set(errors ?? []);
this.performance = performance; this.performance.set(performance);
this.historicalDataItems = chart.map( this.historicalDataItems.set(
({ date, netPerformanceInPercentageWithCurrencyEffect }) => { chart?.map(
return { ({ date, netPerformanceInPercentageWithCurrencyEffect }) => {
date, return {
value: netPerformanceInPercentageWithCurrencyEffect * 100 date,
}; value: (netPerformanceInPercentageWithCurrencyEffect ?? 0) * 100
} };
}
) ?? null
); );
this.precision.set(2);
if ( if (
this.deviceType === 'mobile' && this.deviceType() === 'mobile' &&
this.performance.currentValueInBaseCurrency >= performance.currentValueInBaseCurrency >=
NUMERICAL_PRECISION_THRESHOLD_6_FIGURES NUMERICAL_PRECISION_THRESHOLD_6_FIGURES
) { ) {
this.precision = 0; this.precision.set(0);
} }
this.isLoadingPerformance = false; this.isLoadingPerformance.set(false);
this.changeDetectorRef.markForCheck();
}); });
this.changeDetectorRef.markForCheck();
} }
} }

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

@ -2,16 +2,16 @@
class="align-items-center container d-flex flex-column h-100 justify-content-center overview p-0 position-relative" class="align-items-center container d-flex flex-column h-100 justify-content-center overview p-0 position-relative"
> >
@if ( @if (
!hasImpersonationId && !hasImpersonationId() &&
hasPermissionToCreateActivity && hasPermissionToCreateActivity() &&
user?.activitiesCount === 0 user()?.activitiesCount === 0
) { ) {
<div class="justify-content-center row w-100"> <div class="justify-content-center row w-100">
<div class="col introduction"> <div class="col introduction">
<h4 i18n>Welcome to Ghostfolio</h4> <h4 i18n>Welcome to Ghostfolio</h4>
<p i18n>Ready to take control of your personal finances?</p> <p i18n>Ready to take control of your personal finances?</p>
<ol class="font-weight-bold"> <ol class="font-weight-bold">
<li class="mb-2" [class.text-muted]="user?.accounts?.length > 1"> <li class="mb-2" [class.text-muted]="user()?.accounts?.length > 1">
<a class="d-block" [routerLink]="routerLinkAccounts" <a class="d-block" [routerLink]="routerLinkAccounts"
><span i18n>Setup your accounts</span><br /> ><span i18n>Setup your accounts</span><br />
<span class="font-weight-normal" i18n <span class="font-weight-normal" i18n
@ -40,7 +40,7 @@
</li> </li>
</ol> </ol>
<div class="d-flex justify-content-center"> <div class="d-flex justify-content-center">
@if (user?.accounts?.length === 1) { @if (user()?.accounts?.length === 1) {
<a <a
color="primary" color="primary"
mat-flat-button mat-flat-button
@ -48,7 +48,7 @@
> >
<ng-container i18n>Setup accounts</ng-container> <ng-container i18n>Setup accounts</ng-container>
</a> </a>
} @else if (user?.accounts?.length > 1) { } @else if (user()?.accounts?.length > 1) {
<a <a
color="primary" color="primary"
mat-flat-button mat-flat-button
@ -67,13 +67,13 @@
<gf-line-chart <gf-line-chart
class="position-absolute" class="position-absolute"
unit="%" unit="%"
[class.pr-3]="deviceType === 'mobile'" [class.pr-3]="deviceType() === 'mobile'"
[colorScheme]="user?.settings?.colorScheme" [colorScheme]="user()?.settings?.colorScheme"
[hidden]="historicalDataItems?.length === 0" [hidden]="historicalDataItems()?.length === 0"
[historicalDataItems]="historicalDataItems" [historicalDataItems]="historicalDataItems()"
[isAnimated]="user?.settings?.dateRange === '1d' ? false : true" [isAnimated]="user()?.settings?.dateRange === '1d' ? false : true"
[label]="performanceLabel" [label]="performanceLabel"
[locale]="user?.settings?.locale" [locale]="user()?.settings?.locale"
[showGradient]="true" [showGradient]="true"
[showLoader]="false" [showLoader]="false"
[showXAxis]="false" [showXAxis]="false"
@ -86,16 +86,14 @@
<div class="col"> <div class="col">
<gf-portfolio-performance <gf-portfolio-performance
class="pb-4" class="pb-4"
[deviceType]="deviceType" [deviceType]="deviceType()"
[errors]="errors" [errors]="errors()"
[isAllTimeHigh]="isAllTimeHigh" [isLoading]="isLoadingPerformance()"
[isAllTimeLow]="isAllTimeLow" [locale]="user()?.settings?.locale"
[isLoading]="isLoadingPerformance" [performance]="performance()"
[locale]="user?.settings?.locale" [precision]="precision()"
[performance]="performance" [showDetails]="showDetails()"
[precision]="precision" [unit]="unit()"
[showDetails]="showDetails"
[unit]="unit"
/> />
</div> </div>
</div> </div>

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

@ -24,10 +24,6 @@
} }
<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"
[class]="{
'text-danger': isAllTimeLow,
'text-success': isAllTimeHigh
}"
[hidden]="isLoading" [hidden]="isLoading"
> >
<span #value id="value"></span> <span #value id="value"></span>

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

@ -35,8 +35,6 @@ import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
export class GfPortfolioPerformanceComponent implements OnChanges { export class GfPortfolioPerformanceComponent implements OnChanges {
@Input() deviceType: string; @Input() deviceType: string;
@Input() errors: ResponseError['errors']; @Input() errors: ResponseError['errors'];
@Input() isAllTimeHigh: boolean;
@Input() isAllTimeLow: boolean;
@Input() isLoading: boolean; @Input() isLoading: boolean;
@Input() locale = getLocale(); @Input() locale = getLocale();
@Input() performance: PortfolioPerformance; @Input() performance: PortfolioPerformance;

3
apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts

@ -1,5 +1,6 @@
import { GfFileDropDirective } from '@ghostfolio/client/directives/file-drop/file-drop.directive'; import { GfFileDropDirective } from '@ghostfolio/client/directives/file-drop/file-drop.directive';
import { ImportActivitiesService } from '@ghostfolio/client/services/import-activities.service'; import { ImportActivitiesService } from '@ghostfolio/client/services/import-activities.service';
import { DEFAULT_DATE_RANGE } from '@ghostfolio/common/config';
import { import {
CreateAccountWithBalancesDto, CreateAccountWithBalancesDto,
CreateAssetProfileWithMarketDataDto, CreateAssetProfileWithMarketDataDto,
@ -145,7 +146,7 @@ export class GfImportActivitiesDialogComponent {
type: 'ASSET_CLASS' type: 'ASSET_CLASS'
} }
], ],
range: 'max' range: DEFAULT_DATE_RANGE
}) })
.pipe(takeUntilDestroyed(this.destroyRef)) .pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(({ holdings }) => { .subscribe(({ holdings }) => {

3
libs/common/src/lib/config.ts

@ -2,7 +2,7 @@ import { AssetClass, AssetSubClass, DataSource, Type } from '@prisma/client';
import { JobOptions, JobStatus } from 'bull'; import { JobOptions, JobStatus } from 'bull';
import ms from 'ms'; import ms from 'ms';
import { ColorScheme } from './types'; import { ColorScheme, DateRange } from './types';
export const ghostfolioPrefix = 'GF'; export const ghostfolioPrefix = 'GF';
export const ghostfolioScraperApiSymbolPrefix = `_${ghostfolioPrefix}_`; export const ghostfolioScraperApiSymbolPrefix = `_${ghostfolioPrefix}_`;
@ -82,6 +82,7 @@ export const STATISTICS_GATHERING_QUEUE = 'STATISTICS_GATHERING_QUEUE';
export const DEFAULT_COLOR_SCHEME: ColorScheme = 'LIGHT'; export const DEFAULT_COLOR_SCHEME: ColorScheme = 'LIGHT';
export const DEFAULT_CURRENCY = 'USD'; export const DEFAULT_CURRENCY = 'USD';
export const DEFAULT_DATE_FORMAT_MONTH_YEAR = 'MMM yyyy'; export const DEFAULT_DATE_FORMAT_MONTH_YEAR = 'MMM yyyy';
export const DEFAULT_DATE_RANGE: DateRange = 'max';
export const DEFAULT_HOST = '0.0.0.0'; export const DEFAULT_HOST = '0.0.0.0';
export const DEFAULT_LANGUAGE_CODE = 'en'; export const DEFAULT_LANGUAGE_CODE = 'en';
export const DEFAULT_PAGE_SIZE = 50; export const DEFAULT_PAGE_SIZE = 50;

Loading…
Cancel
Save