diff --git a/CHANGELOG.md b/CHANGELOG.md index a1b922fcb..136f5335a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Integrated the endpoint to get all platforms (`GET api/v1/platforms`) into the create or update account dialog - Removed the deprecated platforms from the info service +- Extracted the scraper configuration to a dedicated tab in the asset profile details dialog of the admin control panel ## 2.227.0 - 2026-01-02 diff --git a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.scss b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.scss index 5e469970e..73c0c0d74 100644 --- a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.scss +++ b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.scss @@ -13,21 +13,5 @@ right: 1rem; top: 0; } - - .mat-expansion-panel { - --mat-expansion-container-background-color: transparent; - - ::ng-deep { - .mat-expansion-panel-body { - padding: 0; - } - } - - .mat-expansion-panel-header { - &:hover { - --mat-expansion-header-hover-state-layer-color: transparent; - } - } - } } } 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 abea236b8..07e060764 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 @@ -38,8 +38,7 @@ import { Inject, OnDestroy, OnInit, - ViewChild, - signal + ViewChild } from '@angular/core'; import { AbstractControl, @@ -60,7 +59,6 @@ import { MatDialogModule, MatDialogRef } from '@angular/material/dialog'; -import { MatExpansionModule } from '@angular/material/expansion'; import { MatInputModule } from '@angular/material/input'; import { MatMenuModule } from '@angular/material/menu'; import { MatSelectModule } from '@angular/material/select'; @@ -79,6 +77,7 @@ import { format } from 'date-fns'; import { StatusCodes } from 'http-status-codes'; import { addIcons } from 'ionicons'; import { + codeSlashOutline, createOutline, ellipsisVertical, readerOutline, @@ -106,7 +105,6 @@ import { AssetProfileDialogParams } from './interfaces/interfaces'; MatButtonModule, MatCheckboxModule, MatDialogModule, - MatExpansionModule, MatInputModule, MatMenuModule, MatSelectModule, @@ -233,8 +231,6 @@ export class GfAssetProfileDialogComponent implements OnDestroy, OnInit { } ]; - public scraperConfiguationIsExpanded = signal(false); - public sectors: { [name: string]: { name: string; value: number }; }; @@ -255,7 +251,13 @@ export class GfAssetProfileDialogComponent implements OnDestroy, OnInit { private snackBar: MatSnackBar, private userService: UserService ) { - addIcons({ createOutline, ellipsisVertical, readerOutline, serverOutline }); + addIcons({ + codeSlashOutline, + createOutline, + ellipsisVertical, + readerOutline, + serverOutline + }); } public get canSaveAssetProfileIdentifier() { @@ -504,7 +506,19 @@ export class GfAssetProfileDialogComponent implements OnDestroy, OnInit { if (!scraperConfiguration.selector || !scraperConfiguration.url) { scraperConfiguration = undefined; } - } catch {} + } catch (error) { + console.error($localize`Could not parse scraper configuration`, error); + + this.snackBar.open( + '😞 ' + $localize`Could not parse scraper configuration`, + undefined, + { + duration: ms('3 seconds') + } + ); + + return; + } try { sectors = JSON.parse(this.assetProfileForm.get('sectors').value); @@ -538,7 +552,16 @@ export class GfAssetProfileDialogComponent implements OnDestroy, OnInit { object: assetProfile }); } catch (error) { - console.error(error); + console.error($localize`Could not validate form`, error); + + this.snackBar.open( + '😞 ' + $localize`Could not validate form`, + undefined, + { + duration: ms('3 seconds') + } + ); + return; } @@ -550,8 +573,29 @@ export class GfAssetProfileDialogComponent implements OnDestroy, OnInit { }, assetProfile ) - .subscribe(() => { - this.initialize(); + .subscribe({ + next: () => { + this.snackBar.open( + '✅ ' + $localize`Asset profile has been saved`, + undefined, + { + duration: ms('3 seconds') + } + ); + + this.initialize(); + }, + error: (error) => { + console.error($localize`Could not save asset profile`, error); + + this.snackBar.open( + '😞 ' + $localize`Could not save asset profile`, + undefined, + { + duration: ms('3 seconds') + } + ); + } }); } @@ -702,8 +746,8 @@ export class GfAssetProfileDialogComponent implements OnDestroy, OnInit { } public onTriggerSubmitAssetProfileForm() { - if (this.assetProfileForm) { - this.assetProfileFormElement.nativeElement.requestSubmit(); + if (this.assetProfileForm.valid) { + this.onSubmitAssetProfileForm(); } } 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 ce0cafbc1..a60e10edc 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 @@ -390,130 +390,6 @@ > - @if (assetProfile?.dataSource === 'MANUAL') { -
- - - - Scraper Configuration - -
-
- - Default Market Price - - -
-
- - HTTP Request Headers - - -
-
- - Locale - - -
-
- - Mode - - @for (modeValue of modeValues; track modeValue) { - {{ - modeValue.viewValue - }} - } - - -
-
- - - Selector* - - - -
-
- - - Url* - - - -
-
- -
-
-
-
-
- } @if (assetProfile?.dataSource === 'MANUAL') {
@@ -588,6 +464,115 @@
+ @if (assetProfile?.dataSource === 'MANUAL') { + + + +
Scraper Configuration
+
+
+
+
+
+ + Default Market Price + + +
+
+ + HTTP Request Headers + + +
+
+ + Locale + + +
+
+ + Mode + + @for (modeValue of modeValues; track modeValue) { + {{ + modeValue.viewValue + }} + } + + +
+
+ + + Selector* + + + +
+
+ + + Url* + + + +
+
+ +
+
+
+
+
+ } diff --git a/apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.component.ts b/apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.component.ts index 5e18f25cf..ceb11a011 100644 --- a/apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.component.ts +++ b/apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.component.ts @@ -59,7 +59,7 @@ export class GfCreateOrUpdateAccountDialogComponent implements OnDestroy { public accountForm: FormGroup; public currencies: string[] = []; public filteredPlatforms: Observable; - public platforms: Platform[]; + public platforms: Platform[] = []; private unsubscribeSubject = new Subject(); @@ -71,10 +71,8 @@ export class GfCreateOrUpdateAccountDialogComponent implements OnDestroy { ) {} public ngOnInit() { - const { currencies, platforms } = this.dataService.fetchInfo(); - + const { currencies } = this.dataService.fetchInfo(); this.currencies = currencies; - this.platforms = platforms; this.accountForm = this.formBuilder.group({ accountId: [{ disabled: true, value: this.data.account.id }], @@ -83,23 +81,33 @@ export class GfCreateOrUpdateAccountDialogComponent implements OnDestroy { currency: [this.data.account.currency, Validators.required], isExcluded: [this.data.account.isExcluded], name: [this.data.account.name, Validators.required], - platformId: [ - this.platforms.find(({ id }) => { - return id === this.data.account.platformId; - }), - this.autocompleteObjectValidator() - ] + platformId: [null, this.autocompleteObjectValidator()] }); - this.filteredPlatforms = this.accountForm - .get('platformId') - .valueChanges.pipe( - startWith(''), - map((value) => { - const name = typeof value === 'string' ? value : value?.name; - return name ? this.filter(name as string) : this.platforms.slice(); - }) + this.dataService.fetchPlatforms().subscribe(({ platforms }) => { + this.platforms = platforms; + + const selectedPlatform = this.platforms.find(({ id }) => { + return id === this.data.account.platformId; + }); + + this.accountForm.patchValue( + { + platformId: selectedPlatform + }, + { emitEvent: false } ); + + this.filteredPlatforms = this.accountForm + .get('platformId') + .valueChanges.pipe( + startWith(''), + map((value) => { + const name = typeof value === 'string' ? value : value?.name; + return name ? this.filter(name as string) : this.platforms.slice(); + }) + ); + }); } public autoCompleteCheck() { diff --git a/apps/client/src/app/services/data.service.ts b/apps/client/src/app/services/data.service.ts index 31b0fef73..21eec06c3 100644 --- a/apps/client/src/app/services/data.service.ts +++ b/apps/client/src/app/services/data.service.ts @@ -42,6 +42,7 @@ import { MarketDataDetailsResponse, MarketDataOfMarketsResponse, OAuthResponse, + PlatformsResponse, PortfolioDetails, PortfolioDividendsResponse, PortfolioHoldingResponse, @@ -521,6 +522,10 @@ export class DataService { ); } + public fetchPlatforms() { + return this.http.get('/api/v1/platforms'); + } + public fetchPortfolioDetails({ filters, withMarkets = false