Browse Source

feat: implements form logic, adujsts backend admin service

pull/4469/head
tobikugel 4 weeks ago
committed by Thomas Kaul
parent
commit
5ce488b49b
  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

@ -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,

2
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

46
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<HTMLFormElement>;
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<AssetProfileDialog>,
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();
}
}
}

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

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

4
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,

Loading…
Cancel
Save