From 5ce488b49b488e05f624d9212801fd1a9413fda9 Mon Sep 17 00:00:00 2001 From: tobikugel Date: Thu, 20 Mar 2025 12:13:36 -0300 Subject: [PATCH] feat: implements form logic, adujsts backend admin service --- apps/api/src/app/admin/admin.service.ts | 74 +++++++++++++------ .../symbol-profile/symbol-profile.service.ts | 2 - .../asset-profile-dialog.component.ts | 46 +++++++++++- .../asset-profile-dialog.html | 3 +- apps/client/src/app/services/admin.service.ts | 4 +- 5 files changed, 99 insertions(+), 30 deletions(-) diff --git a/apps/api/src/app/admin/admin.service.ts b/apps/api/src/app/admin/admin.service.ts index 864f4ecda..ca2ed498d 100644 --- a/apps/api/src/app/admin/admin.service.ts +++ b/apps/api/src/app/admin/admin.service.ts @@ -32,7 +32,12 @@ import { import { Sector } from '@ghostfolio/common/interfaces/sector.interface'; import { MarketDataPreset } from '@ghostfolio/common/types'; -import { BadRequestException, Injectable, Logger } from '@nestjs/common'; +import { + BadRequestException, + HttpException, + Injectable, + Logger +} from '@nestjs/common'; import { AssetClass, AssetSubClass, @@ -42,6 +47,7 @@ import { SymbolProfile } from '@prisma/client'; import { differenceInDays } from 'date-fns'; +import { StatusCodes, getReasonPhrase } from 'http-status-codes'; import { groupBy } from 'lodash'; @Injectable() @@ -487,38 +493,58 @@ export class AdminService { newDataSource && (newSymbol !== symbol || newDataSource !== dataSource) ) { - await this.symbolProfileService.updateAssetProfileIdentifier( - { - dataSource, - symbol - }, + const [profile] = await this.symbolProfileService.getSymbolProfiles([ { dataSource: newDataSource as DataSource, symbol: newSymbol as string } - ); + ]); - await this.marketDataService.updateAssetProfileIdentifier( - { - dataSource, - symbol - }, - { - dataSource: newDataSource as DataSource, - symbol: newSymbol as string - } - ); + if (profile) { + throw new HttpException( + getReasonPhrase(StatusCodes.CONFLICT), + StatusCodes.CONFLICT + ); + } - const [symbolProfile] = await this.symbolProfileService.getSymbolProfiles( - [ + try { + await this.symbolProfileService.updateAssetProfileIdentifier( { - dataSource: dataSource as DataSource, - symbol: symbol as string + dataSource, + symbol + }, + { + dataSource: newDataSource as DataSource, + symbol: newSymbol as string } - ] - ); + ); - return symbolProfile; + await this.marketDataService.updateAssetProfileIdentifier( + { + dataSource, + symbol + }, + { + dataSource: newDataSource as DataSource, + symbol: newSymbol as string + } + ); + + const [symbolProfile] = + await this.symbolProfileService.getSymbolProfiles([ + { + dataSource: newDataSource as DataSource, + symbol: newSymbol as string + } + ]); + + return symbolProfile; + } catch { + throw new HttpException( + getReasonPhrase(StatusCodes.BAD_REQUEST), + StatusCodes.BAD_REQUEST + ); + } } else { const symbolProfileOverrides = { assetClass: assetClass as AssetClass, diff --git a/apps/api/src/services/symbol-profile/symbol-profile.service.ts b/apps/api/src/services/symbol-profile/symbol-profile.service.ts index 65394eef5..c995b13d1 100644 --- a/apps/api/src/services/symbol-profile/symbol-profile.service.ts +++ b/apps/api/src/services/symbol-profile/symbol-profile.service.ts @@ -186,13 +186,11 @@ export class SymbolProfileService { comment, countries, currency, - //dataSource, holdings, isActive, name, scraperConfiguration, sectors, - //symbol, symbolMapping, SymbolProfileOverrides, url diff --git a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts index 443f6a7aa..7d925395f 100644 --- a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts +++ b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts @@ -19,13 +19,17 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, + ElementRef, Inject, OnDestroy, OnInit, + ViewChild, signal } from '@angular/core'; import { FormBuilder, FormControl, Validators } from '@angular/forms'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { MatSnackBar } from '@angular/material/snack-bar'; +import { Router } from '@angular/router'; import { AssetClass, AssetSubClass, @@ -33,6 +37,7 @@ import { SymbolProfile } from '@prisma/client'; import { format } from 'date-fns'; +import ms from 'ms'; import { EMPTY, Subject } from 'rxjs'; import { catchError, takeUntil } from 'rxjs/operators'; @@ -47,6 +52,9 @@ import { AssetProfileDialogParams } from './interfaces/interfaces'; standalone: false }) export class AssetProfileDialog implements OnDestroy, OnInit { + @ViewChild('assetProfileFormElement') + assetProfileFormElement: ElementRef; + public assetProfileClass: string; public assetClasses = Object.keys(AssetClass).map((assetClass) => { return { id: assetClass, label: translate(assetClass) }; @@ -125,6 +133,8 @@ export class AssetProfileDialog implements OnDestroy, OnInit { public dialogRef: MatDialogRef, private formBuilder: FormBuilder, private notificationService: NotificationService, + private router: Router, + private snackBar: MatSnackBar, private userService: UserService ) {} @@ -326,8 +336,23 @@ export class AssetProfileDialog implements OnDestroy, OnInit { .patchAssetProfile(this.data.dataSource, this.data.symbol, { ...assetProfileIdentifierData }) + .pipe( + catchError(() => { + this.snackBar.open('Conflict', undefined, { + duration: ms('3 seconds') + }); + + return EMPTY; + }), + takeUntil(this.unsubscribeSubject) + ) .subscribe(() => { - this.initialize(); + this.onOpenAssetProfileDialog({ + dataSource: assetProfileIdentifierData.dataSource, + symbol: assetProfileIdentifierData.symbol + }); + + this.dialogRef.close(); }); } @@ -482,4 +507,23 @@ export class AssetProfileDialog implements OnDestroy, OnInit { this.unsubscribeSubject.next(); this.unsubscribeSubject.complete(); } + + public onOpenAssetProfileDialog({ + dataSource, + symbol + }: AssetProfileIdentifier) { + this.router.navigate([], { + queryParams: { + dataSource, + symbol, + assetProfileDialog: true + } + }); + } + + public triggerIdentifierSubmit() { + if (this.assetProfileIdentifierForm) { + this.assetProfileFormElement.nativeElement.requestSubmit(); + } + } } diff --git a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html index 4c1fe9ed3..b4effc127 100644 --- a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html +++ b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html @@ -250,6 +250,7 @@ }
Save diff --git a/apps/client/src/app/services/admin.service.ts b/apps/client/src/app/services/admin.service.ts index 7a1e148db..e038f8164 100644 --- a/apps/client/src/app/services/admin.service.ts +++ b/apps/client/src/app/services/admin.service.ts @@ -230,8 +230,8 @@ export class AdminService { countries, currency, name, - newDataSource, - newSymbol, + dataSource: newDataSource, + symbol: newSymbol, scraperConfiguration, sectors, symbolMapping,