From 6c5883349ca7815d33722d70b9e277464ee835bb Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Fri, 2 Jan 2026 14:38:42 +0100 Subject: [PATCH 1/4] Release 2.227.0 (#6134) --- CHANGELOG.md | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 22acc4090..36b516c8c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## Unreleased +## 2.227.0 - 2026-01-02 ### Changed diff --git a/package-lock.json b/package-lock.json index d1a228ade..05e735609 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ghostfolio", - "version": "2.226.0", + "version": "2.227.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ghostfolio", - "version": "2.226.0", + "version": "2.227.0", "hasInstallScript": true, "license": "AGPL-3.0", "dependencies": { diff --git a/package.json b/package.json index b887010cd..1e82ec439 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ghostfolio", - "version": "2.226.0", + "version": "2.227.0", "homepage": "https://ghostfol.io", "license": "AGPL-3.0", "repository": "https://github.com/ghostfolio/ghostfolio", From c52d7f46c26f7bb1caba524e2e64db869118680b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20G=C3=BCnther?= Date: Fri, 2 Jan 2026 19:27:03 +0100 Subject: [PATCH 2/4] Task/move scraper configuration to tab in asset profile dialog (#6094) * Move scraper configuration to tab in asset profile dialog * Update changelog --- CHANGELOG.md | 6 + .../asset-profile-dialog.component.scss | 16 -- .../asset-profile-dialog.component.ts | 70 +++++- .../asset-profile-dialog.html | 233 ++++++++---------- 4 files changed, 172 insertions(+), 153 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 36b516c8c..8cabbc605 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +### Changed + +- 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 ### Changed 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* + + + +
+
+ +
+
+
+
+
+ } From 95cbd01a4c5becac550c69a69f74fa35fcf5502d Mon Sep 17 00:00:00 2001 From: Omkar Gujja <67428719+omkarg01@users.noreply.github.com> Date: Sat, 3 Jan 2026 00:09:18 +0530 Subject: [PATCH 3/4] Task/lazy load platforms via API in create or update account dialog (#6130) * Lazy load platforms via API * Update changelog --- CHANGELOG.md | 1 + ...eate-or-update-account-dialog.component.ts | 44 +++++++++++-------- apps/client/src/app/services/data.service.ts | 5 +++ 3 files changed, 32 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8cabbc605..91fc2bc39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ 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 - 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/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 From 113238f86ee73491ea961672c7c9f4885463ecab Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Sat, 3 Jan 2026 08:48:20 +0100 Subject: [PATCH 4/4] Task/remove deprecated public Stripe key (part 2) (#6138) * Remove deprecated public Stripe key --- libs/common/src/lib/interfaces/info-item.interface.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/libs/common/src/lib/interfaces/info-item.interface.ts b/libs/common/src/lib/interfaces/info-item.interface.ts index c2ee54526..119a94a7c 100644 --- a/libs/common/src/lib/interfaces/info-item.interface.ts +++ b/libs/common/src/lib/interfaces/info-item.interface.ts @@ -18,9 +18,5 @@ export interface InfoItem { platforms: Platform[]; statistics: Statistics; - - /** @deprecated */ - stripePublicKey?: string; - subscriptionOffer?: SubscriptionOffer; }