Browse Source

feat: extend asset profile dialog

pull/2535/head
Dhoni77 2 years ago
committed by Thomas
parent
commit
25052127c2
  1. 6
      apps/api/src/app/admin/admin.service.ts
  2. 16
      apps/api/src/app/admin/update-asset-profile.dto.ts
  3. 14
      apps/api/src/services/symbol-profile/symbol-profile.service.ts
  4. 46
      apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts
  5. 42
      apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html
  6. 2
      apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.module.ts
  7. 12
      apps/client/src/app/services/admin.service.ts

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

@ -303,15 +303,21 @@ export class AdminService {
}
public async patchAssetProfileData({
assetClass,
assetSubClass,
comment,
dataSource,
name,
scraperConfiguration,
symbol,
symbolMapping
}: Prisma.SymbolProfileUpdateInput & UniqueAsset) {
await this.symbolProfileService.updateSymbolProfile({
assetClass,
assetSubClass,
comment,
dataSource,
name,
scraperConfiguration,
symbol,
symbolMapping

16
apps/api/src/app/admin/update-asset-profile.dto.ts

@ -1,11 +1,23 @@
import { Prisma } from '@prisma/client';
import { IsObject, IsOptional, IsString } from 'class-validator';
import { AssetClass, AssetSubClass, Prisma } from '@prisma/client';
import { IsEnum, IsObject, IsOptional, IsString } from 'class-validator';
export class UpdateAssetProfileDto {
@IsEnum(AssetClass, { each: true })
@IsOptional()
assetClass?: AssetClass;
@IsEnum(AssetSubClass, { each: true })
@IsOptional()
assetSubClass?: AssetSubClass;
@IsString()
@IsOptional()
comment?: string;
@IsString()
@IsOptional()
name?: string;
@IsObject()
@IsOptional()
scraperConfiguration?: Prisma.InputJsonObject;

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

@ -13,7 +13,7 @@ import { continents, countries } from 'countries-list';
@Injectable()
export class SymbolProfileService {
public constructor(private readonly prismaService: PrismaService) {}
public constructor(private readonly prismaService: PrismaService) { }
public async add(
assetProfile: Prisma.SymbolProfileCreateInput
@ -86,14 +86,24 @@ export class SymbolProfileService {
}
public updateSymbolProfile({
assetClass,
assetSubClass,
comment,
dataSource,
name,
scraperConfiguration,
symbol,
symbolMapping
}: Prisma.SymbolProfileUpdateInput & UniqueAsset) {
return this.prismaService.symbolProfile.update({
data: { comment, scraperConfiguration, symbolMapping },
data: {
assetClass,
assetSubClass,
comment,
name,
scraperConfiguration,
symbolMapping
},
where: { dataSource_symbol: { dataSource, symbol } }
});
}

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

@ -6,7 +6,7 @@ import {
OnDestroy,
OnInit
} from '@angular/core';
import { FormBuilder } from '@angular/forms';
import { FormBuilder, FormControl, Validators } from '@angular/forms';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { UpdateAssetProfileDto } from '@ghostfolio/api/app/admin/update-asset-profile.dto';
import { AdminService } from '@ghostfolio/client/services/admin.service';
@ -17,7 +17,12 @@ import {
UniqueAsset
} from '@ghostfolio/common/interfaces';
import { translate } from '@ghostfolio/ui/i18n';
import { MarketData, SymbolProfile } from '@prisma/client';
import {
AssetClass,
AssetSubClass,
MarketData,
SymbolProfile
} from '@prisma/client';
import { format, parseISO } from 'date-fns';
import { parse as csvToJson } from 'papaparse';
import { Subject } from 'rxjs';
@ -33,14 +38,23 @@ import { AssetProfileDialogParams } from './interfaces/interfaces';
styleUrls: ['./asset-profile-dialog.component.scss']
})
export class AssetProfileDialog implements OnDestroy, OnInit {
public assetClass: string;
public assetProfileClass: string;
public assetClasses = Object.keys(AssetClass).map((assetClass) => {
return { id: assetClass, label: translate(assetClass) };
});
public assetSubClasses = Object.keys(AssetSubClass).map((assetSubClass) => {
return { id: assetSubClass, label: translate(assetSubClass) };
});
public assetProfile: AdminMarketDataDetails['assetProfile'];
public assetProfileForm = this.formBuilder.group({
assetClass: new FormControl<AssetClass>(undefined),
assetSubClass: new FormControl<AssetSubClass>(undefined),
comment: '',
name: ['', Validators.required],
scraperConfiguration: '',
symbolMapping: ''
});
public assetSubClass: string;
public assetProfileSubClass: string;
public benchmarks: Partial<SymbolProfile>[];
public countries: {
[code: string]: { name: string; value: number };
@ -65,7 +79,7 @@ export class AssetProfileDialog implements OnDestroy, OnInit {
private dataService: DataService,
public dialogRef: MatDialogRef<AssetProfileDialog>,
private formBuilder: FormBuilder
) {}
) { }
public ngOnInit(): void {
this.benchmarks = this.dataService.fetchInfo().benchmarks;
@ -86,8 +100,8 @@ export class AssetProfileDialog implements OnDestroy, OnInit {
.subscribe(({ assetProfile, marketData }) => {
this.assetProfile = assetProfile;
this.assetClass = translate(this.assetProfile?.assetClass);
this.assetSubClass = translate(this.assetProfile?.assetSubClass);
this.assetProfileClass = translate(this.assetProfile?.assetClass);
this.assetProfileSubClass = translate(this.assetProfile?.assetSubClass);
this.countries = {};
this.isBenchmark = this.benchmarks.some(({ id }) => {
return id === this.assetProfile.id;
@ -114,6 +128,9 @@ export class AssetProfileDialog implements OnDestroy, OnInit {
}
this.assetProfileForm.setValue({
name: this.assetProfile.name,
assetClass: this.assetProfile.assetClass,
assetSubClass: this.assetProfile.assetSubClass,
comment: this.assetProfile?.comment ?? '',
scraperConfiguration: JSON.stringify(
this.assetProfile?.scraperConfiguration ?? {}
@ -135,14 +152,14 @@ export class AssetProfileDialog implements OnDestroy, OnInit {
this.adminService
.gatherProfileDataBySymbol({ dataSource, symbol })
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe(() => {});
.subscribe(() => { });
}
public onGatherSymbol({ dataSource, symbol }: UniqueAsset) {
this.adminService
.gatherSymbol({ dataSource, symbol })
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe(() => {});
.subscribe(() => { });
}
public onImportHistoricalData() {
@ -195,18 +212,21 @@ export class AssetProfileDialog implements OnDestroy, OnInit {
scraperConfiguration = JSON.parse(
this.assetProfileForm.controls['scraperConfiguration'].value
);
} catch {}
} catch { }
try {
symbolMapping = JSON.parse(
this.assetProfileForm.controls['symbolMapping'].value
);
} catch {}
} catch { }
const assetProfileData: UpdateAssetProfileDto = {
assetClass: this.assetProfileForm.controls['assetClass'].value,
assetSubClass: this.assetProfileForm.controls['assetSubClass'].value,
comment: this.assetProfileForm.controls['comment'].value ?? null,
name: this.assetProfileForm.controls['name'].value,
scraperConfiguration,
symbolMapping,
comment: this.assetProfileForm.controls['comment'].value ?? null
symbolMapping
};
this.adminService

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

@ -112,7 +112,11 @@
>
</div>
<div class="col-6 mb-3">
<gf-value i18n size="medium" [hidden]="!assetClass" [value]="assetClass"
<gf-value
i18n
size="medium"
[hidden]="!assetProfileClass"
[value]="assetProfileClass"
>Asset Class</gf-value
>
</div>
@ -120,8 +124,8 @@
<gf-value
i18n
size="medium"
[hidden]="!assetSubClass"
[value]="assetSubClass"
[hidden]="!assetProfileSubClass"
[value]="assetProfileSubClass"
>Asset Sub Class</gf-value
>
</div>
@ -174,6 +178,38 @@
</ng-template>
</ng-container>
</div>
<div *ngIf="assetProfile?.dataSource === 'MANUAL'" class="mt-3">
<mat-form-field appearance="outline" class="w-100">
<mat-label i18n>Name</mat-label>
<input formControlName="name" matInput type="text" />
</mat-form-field>
</div>
<div *ngIf="assetProfile?.dataSource === 'MANUAL'" class="mt-3">
<mat-form-field appearance="outline" class="w-100">
<mat-label i18n>Asset Class</mat-label>
<mat-select formControlName="assetClass">
<mat-option [value]="null"></mat-option>
<mat-option
*ngFor="let assetClass of assetClasses"
[value]="assetClass.id"
>{{ assetClass.label }}</mat-option
>
</mat-select>
</mat-form-field>
</div>
<div *ngIf="assetProfile?.dataSource === 'MANUAL'" class="mt-3">
<mat-form-field appearance="outline" class="w-100">
<mat-label i18n>Asset Sub Class</mat-label>
<mat-select formControlName="assetSubClass">
<mat-option [value]="null"></mat-option>
<mat-option
*ngFor="let assetSubClass of assetSubClasses"
[value]="assetSubClass.id"
>{{ assetSubClass.label }}</mat-option
>
</mat-select>
</mat-form-field>
</div>
<div class="d-flex my-3">
<div class="w-50">
<mat-checkbox

2
apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.module.ts

@ -12,6 +12,7 @@ import { GfPortfolioProportionChartModule } from '@ghostfolio/ui/portfolio-propo
import { GfValueModule } from '@ghostfolio/ui/value';
import { AssetProfileDialog } from './asset-profile-dialog.component';
import { MatSelectModule } from '@angular/material/select';
@NgModule({
declarations: [AssetProfileDialog],
@ -26,6 +27,7 @@ import { AssetProfileDialog } from './asset-profile-dialog.component';
MatDialogModule,
MatInputModule,
MatMenuModule,
MatSelectModule,
ReactiveFormsModule,
TextFieldModule
],

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

@ -203,15 +203,25 @@ export class AdminService {
}
public patchAssetProfile({
assetClass,
assetSubClass,
comment,
dataSource,
name,
scraperConfiguration,
symbol,
symbolMapping
}: UniqueAsset & UpdateAssetProfileDto) {
return this.http.patch<EnhancedSymbolProfile>(
`/api/v1/admin/profile-data/${dataSource}/${symbol}`,
{ comment, scraperConfiguration, symbolMapping }
{
assetClass,
assetSubClass,
comment,
name,
scraperConfiguration,
symbolMapping
}
);
}

Loading…
Cancel
Save