Browse Source

feat: implements form logic, adujsts backend admin service

pull/4469/head
tobikugel 1 month ago
parent
commit
a6f9a1dc51
  1. 74
      apps/api/src/app/admin/admin.service.ts
  2. 2
      apps/api/src/services/symbol-profile/symbol-profile.service.ts
  3. 46
      apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts
  4. 3
      apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html
  5. 4
      apps/client/src/app/services/admin.service.ts

74
apps/api/src/app/admin/admin.service.ts

@ -33,7 +33,12 @@ import {
import { Sector } from '@ghostfolio/common/interfaces/sector.interface'; import { Sector } from '@ghostfolio/common/interfaces/sector.interface';
import { MarketDataPreset } from '@ghostfolio/common/types'; import { MarketDataPreset } from '@ghostfolio/common/types';
import { BadRequestException, Injectable, Logger } from '@nestjs/common'; import {
BadRequestException,
HttpException,
Injectable,
Logger
} from '@nestjs/common';
import { import {
AssetClass, AssetClass,
AssetSubClass, AssetSubClass,
@ -44,6 +49,7 @@ import {
SymbolProfile SymbolProfile
} from '@prisma/client'; } from '@prisma/client';
import { differenceInDays } from 'date-fns'; import { differenceInDays } from 'date-fns';
import { StatusCodes, getReasonPhrase } from 'http-status-codes';
import { groupBy } from 'lodash'; import { groupBy } from 'lodash';
@Injectable() @Injectable()
@ -515,38 +521,58 @@ export class AdminService {
newDataSource && newDataSource &&
(newSymbol !== symbol || newDataSource !== dataSource) (newSymbol !== symbol || newDataSource !== dataSource)
) { ) {
await this.symbolProfileService.updateAssetProfileIdentifier( const [profile] = await this.symbolProfileService.getSymbolProfiles([
{
dataSource,
symbol
},
{ {
dataSource: newDataSource as DataSource, dataSource: newDataSource as DataSource,
symbol: newSymbol as string symbol: newSymbol as string
} }
); ]);
await this.marketDataService.updateAssetProfileIdentifier( if (profile) {
{ throw new HttpException(
dataSource, getReasonPhrase(StatusCodes.CONFLICT),
symbol StatusCodes.CONFLICT
}, );
{ }
dataSource: newDataSource as DataSource,
symbol: newSymbol as string
}
);
const [symbolProfile] = await this.symbolProfileService.getSymbolProfiles( try {
[ await this.symbolProfileService.updateAssetProfileIdentifier(
{ {
dataSource: dataSource as DataSource, dataSource,
symbol: symbol as string 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 { } else {
const symbolProfileOverrides = { const symbolProfileOverrides = {
assetClass: assetClass as AssetClass, assetClass: assetClass as AssetClass,

2
apps/api/src/services/symbol-profile/symbol-profile.service.ts

@ -151,12 +151,10 @@ export class SymbolProfileService {
comment, comment,
countries, countries,
currency, currency,
//dataSource,
holdings, holdings,
name, name,
scraperConfiguration, scraperConfiguration,
sectors, sectors,
//symbol,
symbolMapping, symbolMapping,
SymbolProfileOverrides, SymbolProfileOverrides,
url url

46
apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts

@ -19,13 +19,17 @@ import {
ChangeDetectionStrategy, ChangeDetectionStrategy,
ChangeDetectorRef, ChangeDetectorRef,
Component, Component,
ElementRef,
Inject, Inject,
OnDestroy, OnDestroy,
OnInit, OnInit,
ViewChild,
signal signal
} from '@angular/core'; } from '@angular/core';
import { FormBuilder, FormControl, Validators } from '@angular/forms'; import { FormBuilder, FormControl, Validators } from '@angular/forms';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { MatSnackBar } from '@angular/material/snack-bar';
import { Router } from '@angular/router';
import { import {
AssetClass, AssetClass,
AssetSubClass, AssetSubClass,
@ -33,6 +37,7 @@ import {
SymbolProfile SymbolProfile
} from '@prisma/client'; } from '@prisma/client';
import { format } from 'date-fns'; import { format } from 'date-fns';
import ms from 'ms';
import { EMPTY, Subject } from 'rxjs'; import { EMPTY, Subject } from 'rxjs';
import { catchError, takeUntil } from 'rxjs/operators'; import { catchError, takeUntil } from 'rxjs/operators';
@ -47,6 +52,9 @@ import { AssetProfileDialogParams } from './interfaces/interfaces';
standalone: false standalone: false
}) })
export class AssetProfileDialog implements OnDestroy, OnInit { export class AssetProfileDialog implements OnDestroy, OnInit {
@ViewChild('assetProfileFormElement')
assetProfileFormElement: ElementRef<HTMLFormElement>;
public assetProfileClass: string; public assetProfileClass: string;
public assetClasses = Object.keys(AssetClass).map((assetClass) => { public assetClasses = Object.keys(AssetClass).map((assetClass) => {
return { id: assetClass, label: translate(assetClass) }; return { id: assetClass, label: translate(assetClass) };
@ -125,6 +133,8 @@ export class AssetProfileDialog implements OnDestroy, OnInit {
public dialogRef: MatDialogRef<AssetProfileDialog>, public dialogRef: MatDialogRef<AssetProfileDialog>,
private formBuilder: FormBuilder, private formBuilder: FormBuilder,
private notificationService: NotificationService, private notificationService: NotificationService,
private router: Router,
private snackBar: MatSnackBar,
private userService: UserService private userService: UserService
) {} ) {}
@ -326,8 +336,23 @@ export class AssetProfileDialog implements OnDestroy, OnInit {
.patchAssetProfile(this.data.dataSource, this.data.symbol, { .patchAssetProfile(this.data.dataSource, this.data.symbol, {
...assetProfileIdentifierData ...assetProfileIdentifierData
}) })
.pipe(
catchError(() => {
this.snackBar.open('Conflict', undefined, {
duration: ms('3 seconds')
});
return EMPTY;
}),
takeUntil(this.unsubscribeSubject)
)
.subscribe(() => { .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.next();
this.unsubscribeSubject.complete(); 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();
}
}
} }

3
apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html

@ -250,6 +250,7 @@
} }
</div> </div>
<form <form
#assetProfileFormElement
[formGroup]="assetProfileForm" [formGroup]="assetProfileForm"
(keyup.enter)="assetProfileForm.valid && onSubmitAssetProfileForm()" (keyup.enter)="assetProfileForm.valid && onSubmitAssetProfileForm()"
(ngSubmit)="onSubmitAssetProfileForm()" (ngSubmit)="onSubmitAssetProfileForm()"
@ -505,8 +506,8 @@
<button <button
color="primary" color="primary"
mat-flat-button mat-flat-button
type="submit"
[disabled]="!(assetProfileForm.dirty && assetProfileForm.valid)" [disabled]="!(assetProfileForm.dirty && assetProfileForm.valid)"
(click)="triggerIdentifierSubmit()"
> >
<ng-container i18n>Save</ng-container> <ng-container i18n>Save</ng-container>
</button> </button>

4
apps/client/src/app/services/admin.service.ts

@ -243,8 +243,8 @@ export class AdminService {
countries, countries,
currency, currency,
name, name,
newDataSource, dataSource: newDataSource,
newSymbol, symbol: newSymbol,
scraperConfiguration, scraperConfiguration,
sectors, sectors,
symbolMapping, symbolMapping,

Loading…
Cancel
Save