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 6e2dab956..76f6cb9c2 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 @@ -87,6 +87,7 @@ import { readerOutline, serverOutline } from 'ionicons/icons'; +import { isBoolean } from 'lodash'; import ms from 'ms'; import { EMPTY } from 'rxjs'; import { catchError } from 'rxjs/operators'; @@ -129,25 +130,26 @@ export class GfAssetProfileDialogComponent implements OnInit { )};123.45`; @ViewChild('assetProfileFormElement') - assetProfileFormElement: ElementRef; + public readonly assetProfileFormElement: ElementRef; - public assetClassLabel: string; - public assetSubClassLabel: string; + protected assetClassLabel: string; + protected assetSubClassLabel: string; - public assetClassOptions: AssetClassSelectorOption[] = Object.keys(AssetClass) - .map((id) => { - return { id, label: translate(id) } as AssetClassSelectorOption; - }) - .sort((a, b) => { - return a.label.localeCompare(b.label); - }); + protected readonly assetClassOptions: AssetClassSelectorOption[] = + Object.keys(AssetClass) + .map((id) => { + return { id, label: translate(id) } as AssetClassSelectorOption; + }) + .sort((a, b) => { + return a.label.localeCompare(b.label); + }); - public assetSubClassOptions: AssetClassSelectorOption[] = []; - public assetProfile: AdminMarketDataDetails['assetProfile']; + protected assetSubClassOptions: AssetClassSelectorOption[] = []; + protected assetProfile: AdminMarketDataDetails['assetProfile']; - public assetProfileForm = this.formBuilder.group({ - assetClass: new FormControl(undefined), - assetSubClass: new FormControl(undefined), + protected readonly assetProfileForm = this.formBuilder.group({ + assetClass: new FormControl(null), + assetSubClass: new FormControl(null), comment: '', countries: ['', jsonValidator()], currency: '', @@ -156,11 +158,15 @@ export class GfAssetProfileDialogComponent implements OnInit { }), isActive: [true], name: ['', Validators.required], - scraperConfiguration: this.formBuilder.group({ - defaultMarketPrice: null, - headers: [JSON.stringify({}), jsonValidator()], + scraperConfiguration: this.formBuilder.group< + Omit & { + headers: FormControl; + } + >({ + defaultMarketPrice: undefined, + headers: new FormControl(JSON.stringify({}), jsonValidator()), locale: '', - mode: '', + mode: 'lazy', selector: '', url: '' }), @@ -169,12 +175,11 @@ export class GfAssetProfileDialogComponent implements OnInit { url: '' }); - public assetProfileIdentifierForm = this.formBuilder.group( + protected readonly assetProfileIdentifierForm = this.formBuilder.group( { - assetProfileIdentifier: new FormControl( - { symbol: null, dataSource: null }, - [Validators.required] - ) + assetProfileIdentifier: new FormControl< + AssetProfileIdentifier | { dataSource: null; symbol: null } + >({ dataSource: null, symbol: null }, [Validators.required]) }, { validators: (control) => { @@ -183,16 +188,15 @@ export class GfAssetProfileDialogComponent implements OnInit { } ); - public benchmarks: Partial[]; - public canEditAssetProfile = true; + protected canEditAssetProfile = true; - public countries: { + protected countries: { [code: string]: { name: string; value: number }; }; - public currencies: string[] = []; + protected currencies: string[] = []; - public dateRangeOptions = [ + protected readonly dateRangeOptions = [ { label: $localize`Current week` + ' (' + $localize`WTD` + ')', value: 'wtd' @@ -218,14 +222,14 @@ export class GfAssetProfileDialogComponent implements OnInit { value: 'max' } ]; - public historicalDataItems: LineChartItem[]; - public isBenchmark = false; - public isDataGatheringEnabled: boolean; - public isEditAssetProfileIdentifierMode = false; - public isUUID = isUUID; - public marketDataItems: MarketData[] = []; - - public modeValues = [ + protected historicalDataItems: LineChartItem[]; + protected isBenchmark = false; + protected isDataGatheringEnabled: boolean; + protected isEditAssetProfileIdentifierMode = false; + protected readonly isUUID = isUUID; + protected marketDataItems: MarketData[] = []; + + protected readonly modeValues = [ { value: 'lazy', viewValue: $localize`Lazy` + ' (' + $localize`end of day` + ')' @@ -236,20 +240,22 @@ export class GfAssetProfileDialogComponent implements OnInit { } ]; - public sectors: { + protected sectors: { [name: string]: { name: string; value: number }; }; - public user: User; + protected user: User; + + private benchmarks: Partial[]; public constructor( - public adminMarketDataService: AdminMarketDataService, + protected adminMarketDataService: AdminMarketDataService, private adminService: AdminService, private changeDetectorRef: ChangeDetectorRef, - @Inject(MAT_DIALOG_DATA) public data: AssetProfileDialogParams, + @Inject(MAT_DIALOG_DATA) protected data: AssetProfileDialogParams, private dataService: DataService, private destroyRef: DestroyRef, - public dialogRef: MatDialogRef, + private dialogRef: MatDialogRef, private formBuilder: FormBuilder, private notificationService: NotificationService, private snackBar: MatSnackBar, @@ -264,7 +270,7 @@ export class GfAssetProfileDialogComponent implements OnInit { }); } - public get canSaveAssetProfileIdentifier() { + protected get canSaveAssetProfileIdentifier() { return !this.assetProfileForm.dirty && this.canEditAssetProfile; } @@ -277,8 +283,8 @@ export class GfAssetProfileDialogComponent implements OnInit { this.initialize(); } - public initialize() { - this.historicalDataItems = undefined; + protected initialize() { + this.historicalDataItems = []; this.adminService .fetchAdminData() @@ -298,10 +304,13 @@ export class GfAssetProfileDialogComponent implements OnInit { } }); - this.assetProfileForm - .get('assetClass') - .valueChanges.pipe(takeUntilDestroyed(this.destroyRef)) + this.assetProfileForm.controls.assetClass.valueChanges + .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe((assetClass) => { + if (!assetClass) { + return; + } + const assetSubClasses = ASSET_CLASS_MAPPING.get(assetClass) ?? []; this.assetSubClassOptions = assetSubClasses @@ -313,7 +322,7 @@ export class GfAssetProfileDialogComponent implements OnInit { }) .sort((a, b) => a.label.localeCompare(b.label)); - this.assetProfileForm.get('assetSubClass').setValue(null); + this.assetProfileForm.controls.assetSubClass.setValue(null); this.changeDetectorRef.markForCheck(); }); @@ -327,8 +336,10 @@ export class GfAssetProfileDialogComponent implements OnInit { .subscribe(({ assetProfile, marketData }) => { this.assetProfile = assetProfile; - this.assetClassLabel = translate(this.assetProfile?.assetClass); - this.assetSubClassLabel = translate(this.assetProfile?.assetSubClass); + this.assetClassLabel = translate(this.assetProfile?.assetClass ?? ''); + this.assetSubClassLabel = translate( + this.assetProfile?.assetSubClass ?? '' + ); this.canEditAssetProfile = !isCurrency( getCurrencyFromSymbol(this.data.symbol) @@ -350,7 +361,10 @@ export class GfAssetProfileDialogComponent implements OnInit { this.marketDataItems = marketData; this.sectors = {}; - if (this.assetProfile?.countries?.length > 0) { + if ( + this.assetProfile?.countries && + this.assetProfile.countries.length > 0 + ) { for (const { code, name, weight } of this.assetProfile.countries) { this.countries[code] = { name, @@ -359,7 +373,10 @@ export class GfAssetProfileDialogComponent implements OnInit { } } - if (this.assetProfile?.sectors?.length > 0) { + if ( + this.assetProfile?.sectors && + this.assetProfile.sectors.length > 0 + ) { for (const { name, weight } of this.assetProfile.sectors) { this.sectors[name] = { name, @@ -377,12 +394,14 @@ export class GfAssetProfileDialogComponent implements OnInit { return { code, weight }; }) ?? [] ), - currency: this.assetProfile?.currency, + currency: this.assetProfile?.currency ?? null, historicalData: { csvString: GfAssetProfileDialogComponent.HISTORICAL_DATA_TEMPLATE }, - isActive: this.assetProfile?.isActive, - name: this.assetProfile.name ?? this.assetProfile.symbol, + isActive: isBoolean(this.assetProfile?.isActive) + ? this.assetProfile.isActive + : null, + name: this.assetProfile.name ?? this.assetProfile.symbol ?? null, scraperConfiguration: { defaultMarketPrice: this.assetProfile?.scraperConfiguration?.defaultMarketPrice ?? @@ -410,7 +429,7 @@ export class GfAssetProfileDialogComponent implements OnInit { }); } - public onCancelEditAssetProfileIdentifierMode() { + protected onCancelEditAssetProfileIdentifierMode() { this.isEditAssetProfileIdentifierMode = false; if (this.canEditAssetProfile) { @@ -420,17 +439,20 @@ export class GfAssetProfileDialogComponent implements OnInit { this.assetProfileIdentifierForm.reset(); } - public onClose() { + protected onClose() { this.dialogRef.close(); } - public onDeleteProfileData({ dataSource, symbol }: AssetProfileIdentifier) { + protected onDeleteProfileData({ + dataSource, + symbol + }: AssetProfileIdentifier) { this.adminMarketDataService.deleteAssetProfile({ dataSource, symbol }); this.dialogRef.close(); } - public onGatherProfileDataBySymbol({ + protected onGatherProfileDataBySymbol({ dataSource, symbol }: AssetProfileIdentifier) { @@ -440,7 +462,7 @@ export class GfAssetProfileDialogComponent implements OnInit { .subscribe(); } - public onGatherSymbol({ + protected onGatherSymbol({ dataSource, range, symbol @@ -453,13 +475,13 @@ export class GfAssetProfileDialogComponent implements OnInit { .subscribe(); } - public onMarketDataChanged(withRefresh: boolean = false) { + protected onMarketDataChanged(withRefresh: boolean = false) { if (withRefresh) { this.initialize(); } } - public onSetBenchmark({ dataSource, symbol }: AssetProfileIdentifier) { + protected onSetBenchmark({ dataSource, symbol }: AssetProfileIdentifier) { this.dataService .postBenchmark({ dataSource, symbol }) .pipe(takeUntilDestroyed(this.destroyRef)) @@ -472,50 +494,48 @@ export class GfAssetProfileDialogComponent implements OnInit { }); } - public onSetEditAssetProfileIdentifierMode() { + protected onSetEditAssetProfileIdentifierMode() { this.isEditAssetProfileIdentifierMode = true; this.assetProfileForm.disable(); } - public async onSubmitAssetProfileForm() { - let countries = []; - let scraperConfiguration: ScraperConfiguration = { + protected async onSubmitAssetProfileForm() { + let countries: Prisma.InputJsonArray = []; + let scraperConfiguration: Prisma.InputJsonObject | undefined = { selector: '', url: '' }; - let sectors = []; - let symbolMapping = {}; + let sectors: Prisma.InputJsonArray = []; + let symbolMapping: Record = {}; try { - countries = JSON.parse(this.assetProfileForm.get('countries').value); + countries = JSON.parse( + this.assetProfileForm.controls.countries.value ?? '[]' + ) as Prisma.InputJsonArray; } catch {} try { scraperConfiguration = { defaultMarketPrice: - (this.assetProfileForm.controls['scraperConfiguration'].controls[ - 'defaultMarketPrice' - ].value as number) || undefined, + this.assetProfileForm.controls.scraperConfiguration.controls + .defaultMarketPrice?.value ?? undefined, headers: JSON.parse( - this.assetProfileForm.controls['scraperConfiguration'].controls[ - 'headers' - ].value - ), + this.assetProfileForm.controls.scraperConfiguration.controls.headers + .value ?? '{}' + ) as Record, locale: - this.assetProfileForm.controls['scraperConfiguration'].controls[ - 'locale' - ].value || undefined, - mode: this.assetProfileForm.controls['scraperConfiguration'].controls[ - 'mode' - ].value as ScraperConfiguration['mode'], + this.assetProfileForm.controls.scraperConfiguration.controls.locale + ?.value ?? undefined, + mode: + this.assetProfileForm.controls.scraperConfiguration.controls.mode + ?.value ?? undefined, selector: - this.assetProfileForm.controls['scraperConfiguration'].controls[ - 'selector' - ].value, - url: this.assetProfileForm.controls['scraperConfiguration'].controls[ - 'url' - ].value + this.assetProfileForm.controls.scraperConfiguration.controls.selector + .value ?? '', + url: + this.assetProfileForm.controls.scraperConfiguration.controls.url + .value ?? '' }; if (!scraperConfiguration.selector || !scraperConfiguration.url) { @@ -536,28 +556,32 @@ export class GfAssetProfileDialogComponent implements OnInit { } try { - sectors = JSON.parse(this.assetProfileForm.get('sectors').value); + sectors = JSON.parse( + this.assetProfileForm.controls.sectors.value ?? '[]' + ) as Prisma.InputJsonArray; } catch {} try { symbolMapping = JSON.parse( - this.assetProfileForm.get('symbolMapping').value - ); + this.assetProfileForm.controls.symbolMapping.value ?? '{}' + ) as Record; } catch {} const assetProfile: UpdateAssetProfileDto = { countries, + scraperConfiguration, sectors, symbolMapping, - assetClass: this.assetProfileForm.get('assetClass').value, - assetSubClass: this.assetProfileForm.get('assetSubClass').value, - comment: this.assetProfileForm.get('comment').value || null, - currency: this.assetProfileForm.get('currency').value, - isActive: this.assetProfileForm.get('isActive').value, - name: this.assetProfileForm.get('name').value, - scraperConfiguration: - scraperConfiguration as unknown as Prisma.InputJsonObject, - url: this.assetProfileForm.get('url').value || null + assetClass: this.assetProfileForm.controls.assetClass.value ?? undefined, + assetSubClass: + this.assetProfileForm.controls.assetSubClass.value ?? undefined, + comment: this.assetProfileForm.controls.comment.value ?? undefined, + currency: this.assetProfileForm.controls.currency.value ?? undefined, + isActive: isBoolean(this.assetProfileForm.controls.isActive.value) + ? this.assetProfileForm.controls.isActive.value + : undefined, + name: this.assetProfileForm.controls.name.value ?? undefined, + url: this.assetProfileForm.controls.url.value ?? undefined }; try { @@ -600,7 +624,7 @@ export class GfAssetProfileDialogComponent implements OnInit { this.initialize(); }, - error: (error) => { + error: (error: HttpErrorResponse) => { console.error($localize`Could not save asset profile`, error); this.snackBar.open( @@ -614,12 +638,14 @@ export class GfAssetProfileDialogComponent implements OnInit { }); } - public async onSubmitAssetProfileIdentifierForm() { + protected async onSubmitAssetProfileIdentifierForm() { const assetProfileIdentifier: UpdateAssetProfileDto = { - dataSource: this.assetProfileIdentifierForm.get('assetProfileIdentifier') - .value.dataSource, - symbol: this.assetProfileIdentifierForm.get('assetProfileIdentifier') - .value.symbol + dataSource: + this.assetProfileIdentifierForm.controls.assetProfileIdentifier.value + ?.dataSource ?? undefined, + symbol: + this.assetProfileIdentifierForm.controls.assetProfileIdentifier.value + ?.symbol ?? undefined }; try { @@ -676,38 +702,33 @@ export class GfAssetProfileDialogComponent implements OnInit { }); } - public onTestMarketData() { + protected onTestMarketData() { this.adminService .testMarketData({ dataSource: this.data.dataSource, scraperConfiguration: { - defaultMarketPrice: this.assetProfileForm.controls[ - 'scraperConfiguration' - ].controls['defaultMarketPrice'].value as number, + defaultMarketPrice: + this.assetProfileForm.controls.scraperConfiguration.controls + .defaultMarketPrice?.value, headers: JSON.parse( - this.assetProfileForm.controls['scraperConfiguration'].controls[ - 'headers' - ].value - ), + this.assetProfileForm.controls.scraperConfiguration.controls.headers + .value ?? '{}' + ) as Record, locale: - this.assetProfileForm.controls['scraperConfiguration'].controls[ - 'locale' - ].value || undefined, - mode: this.assetProfileForm.controls['scraperConfiguration'].controls[ - 'mode' - ].value, + this.assetProfileForm.controls.scraperConfiguration.controls.locale + ?.value || undefined, + mode: this.assetProfileForm.controls.scraperConfiguration.controls + .mode?.value, selector: - this.assetProfileForm.controls['scraperConfiguration'].controls[ - 'selector' - ].value, - url: this.assetProfileForm.controls['scraperConfiguration'].controls[ - 'url' - ].value + this.assetProfileForm.controls.scraperConfiguration.controls + .selector.value, + url: this.assetProfileForm.controls.scraperConfiguration.controls.url + .value }, symbol: this.data.symbol }) .pipe( - catchError(({ error }) => { + catchError(({ error }: HttpErrorResponse) => { this.notificationService.alert({ message: error?.message, title: $localize`Error` @@ -723,26 +744,26 @@ export class GfAssetProfileDialogComponent implements OnInit { ' ' + price + ' ' + - this.assetProfileForm.get('currency').value + this.assetProfileForm.controls.currency.value }); }); } - public onToggleIsActive({ checked }: MatCheckboxChange) { + protected onToggleIsActive({ checked }: MatCheckboxChange) { if (checked) { - this.assetProfileForm.get('isActive')?.setValue(true); + this.assetProfileForm.controls.isActive.setValue(true); } else { - this.assetProfileForm.get('isActive')?.setValue(false); + this.assetProfileForm.controls.isActive.setValue(false); } if (checked === this.assetProfile.isActive) { - this.assetProfileForm.get('isActive')?.markAsPristine(); + this.assetProfileForm.controls.isActive.markAsPristine(); } else { - this.assetProfileForm.get('isActive')?.markAsDirty(); + this.assetProfileForm.controls.isActive.markAsDirty(); } } - public onUnsetBenchmark({ dataSource, symbol }: AssetProfileIdentifier) { + protected onUnsetBenchmark({ dataSource, symbol }: AssetProfileIdentifier) { this.dataService .deleteBenchmark({ dataSource, symbol }) .pipe(takeUntilDestroyed(this.destroyRef)) @@ -755,15 +776,15 @@ export class GfAssetProfileDialogComponent implements OnInit { }); } - public onTriggerSubmitAssetProfileForm() { + protected onTriggerSubmitAssetProfileForm() { if (this.assetProfileForm.valid) { this.onSubmitAssetProfileForm(); } } - private isNewSymbolValid(control: AbstractControl): ValidationErrors { + private isNewSymbolValid(control: AbstractControl): ValidationErrors | null { const currentAssetProfileIdentifier: AssetProfileIdentifier | undefined = - control.get('assetProfileIdentifier').value; + control.get('assetProfileIdentifier')?.value; if ( currentAssetProfileIdentifier?.dataSource === this.data?.dataSource && @@ -773,5 +794,7 @@ export class GfAssetProfileDialogComponent implements OnInit { equalsPreviousProfileIdentifier: true }; } + + return null; } } 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 9ae7f8064..cfae840a8 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 @@ -419,11 +419,11 @@ Url - @if (assetProfileForm.get('url').value) { + @if (assetProfileForm.controls.url.value) { }