From ec032f3a61306fa9192b74532feee76d6c35418b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20G=C3=BCnther?= Date: Sun, 31 Aug 2025 15:15:49 +0200 Subject: [PATCH] apply feedback - Move gatherSymbol logic from admin-market-data component to create-asset-profile-dialog onSubmit method - Replace hardcoded 'USD' with DEFAULT_CURRENCY constant for better maintainability - Extend AdminData interface to include useForExchangeRates flag in dataProviders array - Update server-side admin service to populate useForExchangeRates based on exchange rate data source - Use proper exchange rate data source instead of hardcoded 'MANUAL' for currency operations - Improve currency handling by automatically calling gatherSymbol after currency addition - Clean up unused imports and fix TypeScript types --- apps/api/src/app/admin/admin.service.ts | 5 +- .../admin-market-data.component.ts | 14 +----- .../create-asset-profile-dialog.component.ts | 46 ++++++++++++++++--- .../lib/interfaces/admin-data.interface.ts | 5 +- 4 files changed, 49 insertions(+), 21 deletions(-) diff --git a/apps/api/src/app/admin/admin.service.ts b/apps/api/src/app/admin/admin.service.ts index d07e74013..50b80df70 100644 --- a/apps/api/src/app/admin/admin.service.ts +++ b/apps/api/src/app/admin/admin.service.ts @@ -159,7 +159,10 @@ export class AdminService { return { ...dataProviderInfo, - assetProfileCount + assetProfileCount, + useForExchangeRates: + dataSource === + this.dataProviderService.getDataSourceForExchangeRates() }; } diff --git a/apps/client/src/app/components/admin-market-data/admin-market-data.component.ts b/apps/client/src/app/components/admin-market-data/admin-market-data.component.ts index 84fa098a7..4e410c3a0 100644 --- a/apps/client/src/app/components/admin-market-data/admin-market-data.component.ts +++ b/apps/client/src/app/components/admin-market-data/admin-market-data.component.ts @@ -63,7 +63,7 @@ import { } from 'ionicons/icons'; import { DeviceDetectorService } from 'ngx-device-detector'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; -import { Subject, of } from 'rxjs'; +import { Subject } from 'rxjs'; import { distinctUntilChanged, switchMap, takeUntil } from 'rxjs/operators'; import { AdminMarketDataService } from './admin-market-data.service'; @@ -480,21 +480,11 @@ export class GfAdminMarketDataComponent dialogRef .afterClosed() .pipe(takeUntil(this.unsubscribeSubject)) - .subscribe(({ dataSource, symbol, isCurrency } = {}) => { + .subscribe(({ dataSource, symbol } = {}) => { if (dataSource && symbol) { this.adminService .addAssetProfile({ dataSource, symbol }) .pipe( - switchMap(() => { - // Call gatherSymbol for currencies to collect historical data - if (isCurrency) { - return this.adminService.gatherSymbol({ - dataSource, - symbol - }); - } - return of(null); - }), switchMap(() => { this.isLoading = true; this.changeDetectorRef.markForCheck(); diff --git a/apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.component.ts b/apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.component.ts index 0fe898881..b4403fe3d 100644 --- a/apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.component.ts +++ b/apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.component.ts @@ -1,6 +1,7 @@ import { AdminService } from '@ghostfolio/client/services/admin.service'; import { DataService } from '@ghostfolio/client/services/data.service'; import { + DEFAULT_CURRENCY, ghostfolioPrefix, PROPERTY_CURRENCIES } from '@ghostfolio/common/config'; @@ -29,8 +30,9 @@ import { MatDialogModule, MatDialogRef } from '@angular/material/dialog'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; import { MatRadioModule } from '@angular/material/radio'; +import { DataSource } from '@prisma/client'; import { isISO4217CurrencyCode } from 'class-validator'; -import { Subject, takeUntil } from 'rxjs'; +import { Subject, switchMap, takeUntil } from 'rxjs'; import { CreateAssetProfileDialogMode } from './interfaces/interfaces'; @@ -56,6 +58,7 @@ export class GfCreateAssetProfileDialogComponent implements OnInit, OnDestroy { public mode: CreateAssetProfileDialogMode; private customCurrencies: string[]; + private exchangeRateDataSource: DataSource; private unsubscribeSubject = new Subject(); public constructor( @@ -68,6 +71,7 @@ export class GfCreateAssetProfileDialogComponent implements OnInit, OnDestroy { public ngOnInit() { this.initializeCustomCurrencies(); + this.initializeExchangeRateDataSource(); this.createAssetProfileForm = this.formBuilder.group( { @@ -115,13 +119,25 @@ export class GfCreateAssetProfileDialogComponent implements OnInit, OnDestroy { .putAdminSetting(PROPERTY_CURRENCIES, { value: JSON.stringify(currencies) }) - .pipe(takeUntil(this.unsubscribeSubject)) + .pipe( + switchMap(() => { + // Add the currency asset profile first + return this.adminService.addAssetProfile({ + dataSource: this.exchangeRateDataSource, + symbol: `${currency}${DEFAULT_CURRENCY}` + }); + }), + switchMap(() => { + // Then gather historical data for the currency + return this.adminService.gatherSymbol({ + dataSource: this.exchangeRateDataSource, + symbol: `${currency}${DEFAULT_CURRENCY}` + }); + }), + takeUntil(this.unsubscribeSubject) + ) .subscribe(() => { - this.dialogRef.close({ - dataSource: 'MANUAL', - symbol: `${currency}USD`, - isCurrency: true - }); + this.dialogRef.close(); }); } else if (this.mode === 'manual') { this.dialogRef.close({ @@ -185,6 +201,22 @@ export class GfCreateAssetProfileDialogComponent implements OnInit, OnDestroy { }); } + private initializeExchangeRateDataSource() { + this.adminService + .fetchAdminData() + .pipe(takeUntil(this.unsubscribeSubject)) + .subscribe(({ dataProviders }) => { + const exchangeRateProvider = dataProviders.find( + (provider) => provider.useForExchangeRates + ); + + this.exchangeRateDataSource = + exchangeRateProvider?.dataSource || DataSource.MANUAL; + + this.changeDetectorRef.markForCheck(); + }); + } + private iso4217CurrencyCodeValidator(): ValidatorFn { return (control: AbstractControl): ValidationErrors | null => { if (!isISO4217CurrencyCode(control.value?.toUpperCase())) { diff --git a/libs/common/src/lib/interfaces/admin-data.interface.ts b/libs/common/src/lib/interfaces/admin-data.interface.ts index cef983902..23821a86b 100644 --- a/libs/common/src/lib/interfaces/admin-data.interface.ts +++ b/libs/common/src/lib/interfaces/admin-data.interface.ts @@ -1,7 +1,10 @@ import { DataProviderInfo } from './data-provider-info.interface'; export interface AdminData { - dataProviders: (DataProviderInfo & { assetProfileCount: number })[]; + dataProviders: (DataProviderInfo & { + assetProfileCount: number; + useForExchangeRates: boolean; + })[]; settings: { [key: string]: boolean | object | string | string[] }; transactionCount: number; userCount: number;