From dcd2d179085b88d7401113efe5be3f81083da9a9 Mon Sep 17 00:00:00 2001 From: SK Akram Date: Wed, 1 Oct 2025 12:39:14 +0530 Subject: [PATCH 01/24] Task/prefix create watchlist item dialog component with Gf (#5617) * Prefix create watchlist item dialog component --- .../create-watchlist-item-dialog.component.ts | 4 ++-- .../home-watchlist.component.ts | 21 +++++++++++-------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/apps/client/src/app/components/home-watchlist/create-watchlist-item-dialog/create-watchlist-item-dialog.component.ts b/apps/client/src/app/components/home-watchlist/create-watchlist-item-dialog/create-watchlist-item-dialog.component.ts index 759e47a01..7bd7d2ae1 100644 --- a/apps/client/src/app/components/home-watchlist/create-watchlist-item-dialog/create-watchlist-item-dialog.component.ts +++ b/apps/client/src/app/components/home-watchlist/create-watchlist-item-dialog/create-watchlist-item-dialog.component.ts @@ -36,13 +36,13 @@ import { Subject } from 'rxjs'; styleUrls: ['./create-watchlist-item-dialog.component.scss'], templateUrl: 'create-watchlist-item-dialog.html' }) -export class CreateWatchlistItemDialogComponent implements OnInit, OnDestroy { +export class GfCreateWatchlistItemDialogComponent implements OnInit, OnDestroy { public createWatchlistItemForm: FormGroup; private unsubscribeSubject = new Subject(); public constructor( - public readonly dialogRef: MatDialogRef, + public readonly dialogRef: MatDialogRef, public readonly formBuilder: FormBuilder ) {} diff --git a/apps/client/src/app/components/home-watchlist/home-watchlist.component.ts b/apps/client/src/app/components/home-watchlist/home-watchlist.component.ts index 226d748cd..efe93a843 100644 --- a/apps/client/src/app/components/home-watchlist/home-watchlist.component.ts +++ b/apps/client/src/app/components/home-watchlist/home-watchlist.component.ts @@ -28,7 +28,7 @@ import { DeviceDetectorService } from 'ngx-device-detector'; import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; -import { CreateWatchlistItemDialogComponent } from './create-watchlist-item-dialog/create-watchlist-item-dialog.component'; +import { GfCreateWatchlistItemDialogComponent } from './create-watchlist-item-dialog/create-watchlist-item-dialog.component'; import { CreateWatchlistItemDialogParams } from './create-watchlist-item-dialog/interfaces/interfaces'; @Component({ @@ -149,14 +149,17 @@ export class HomeWatchlistComponent implements OnDestroy, OnInit { .subscribe((user) => { this.user = user; - const dialogRef = this.dialog.open(CreateWatchlistItemDialogComponent, { - autoFocus: false, - data: { - deviceType: this.deviceType, - locale: this.user?.settings?.locale - } as CreateWatchlistItemDialogParams, - width: this.deviceType === 'mobile' ? '100vw' : '50rem' - }); + const dialogRef = this.dialog.open( + GfCreateWatchlistItemDialogComponent, + { + autoFocus: false, + data: { + deviceType: this.deviceType, + locale: this.user?.settings?.locale + } as CreateWatchlistItemDialogParams, + width: this.deviceType === 'mobile' ? '100vw' : '50rem' + } + ); dialogRef .afterClosed() From ce1560751ea15142620b0c7ea302c0a0af724064 Mon Sep 17 00:00:00 2001 From: CodeySnooze <234940681+CodeySnooze@users.noreply.github.com> Date: Wed, 1 Oct 2025 17:31:32 +0530 Subject: [PATCH 02/24] Task/add component suffix to import activities dialog component (#5620) * Add component suffix --- .../pages/portfolio/activities/activities-page.component.ts | 6 +++--- .../import-activities-dialog.component.ts | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/client/src/app/pages/portfolio/activities/activities-page.component.ts b/apps/client/src/app/pages/portfolio/activities/activities-page.component.ts index a722dffbf..d251407cf 100644 --- a/apps/client/src/app/pages/portfolio/activities/activities-page.component.ts +++ b/apps/client/src/app/pages/portfolio/activities/activities-page.component.ts @@ -28,7 +28,7 @@ import { Subject, Subscription } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; import { GfCreateOrUpdateActivityDialog } from './create-or-update-activity-dialog/create-or-update-activity-dialog.component'; -import { GfImportActivitiesDialog } from './import-activities-dialog/import-activities-dialog.component'; +import { GfImportActivitiesDialogComponent } from './import-activities-dialog/import-activities-dialog.component'; import { ImportActivitiesDialogParams } from './import-activities-dialog/interfaces/interfaces'; @Component({ @@ -245,7 +245,7 @@ export class GfActivitiesPageComponent implements OnDestroy, OnInit { } public onImport() { - const dialogRef = this.dialog.open(GfImportActivitiesDialog, { + const dialogRef = this.dialog.open(GfImportActivitiesDialogComponent, { data: { deviceType: this.deviceType, user: this.user @@ -268,7 +268,7 @@ export class GfActivitiesPageComponent implements OnDestroy, OnInit { } public onImportDividends() { - const dialogRef = this.dialog.open(GfImportActivitiesDialog, { + const dialogRef = this.dialog.open(GfImportActivitiesDialogComponent, { data: { activityTypes: ['DIVIDEND'], deviceType: this.deviceType, diff --git a/apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts b/apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts index ea3292663..228eac5c3 100644 --- a/apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts +++ b/apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts @@ -78,7 +78,7 @@ import { ImportActivitiesDialogParams } from './interfaces/interfaces'; styleUrls: ['./import-activities-dialog.scss'], templateUrl: 'import-activities-dialog.html' }) -export class GfImportActivitiesDialog implements OnDestroy { +export class GfImportActivitiesDialogComponent implements OnDestroy { public accounts: CreateAccountWithBalancesDto[] = []; public activities: Activity[] = []; public assetProfileForm: FormGroup; @@ -109,7 +109,7 @@ export class GfImportActivitiesDialog implements OnDestroy { private dataService: DataService, private deviceService: DeviceDetectorService, private formBuilder: FormBuilder, - public dialogRef: MatDialogRef, + public dialogRef: MatDialogRef, private importActivitiesService: ImportActivitiesService, private snackBar: MatSnackBar ) { From 5375d0fee10cd5d2bf418112fc216a77d649750a Mon Sep 17 00:00:00 2001 From: Anirban Biswas <139000437+Rustix69@users.noreply.github.com> Date: Wed, 1 Oct 2025 17:58:27 +0530 Subject: [PATCH 03/24] Task/add component suffix to create or update activity dialog component (#5628) * Add component suffix --- .../activities/activities-page.component.ts | 58 ++++++++++--------- ...ate-or-update-activity-dialog.component.ts | 4 +- 2 files changed, 34 insertions(+), 28 deletions(-) diff --git a/apps/client/src/app/pages/portfolio/activities/activities-page.component.ts b/apps/client/src/app/pages/portfolio/activities/activities-page.component.ts index d251407cf..ce99fbf77 100644 --- a/apps/client/src/app/pages/portfolio/activities/activities-page.component.ts +++ b/apps/client/src/app/pages/portfolio/activities/activities-page.component.ts @@ -27,7 +27,7 @@ import { DeviceDetectorService } from 'ngx-device-detector'; import { Subject, Subscription } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; -import { GfCreateOrUpdateActivityDialog } from './create-or-update-activity-dialog/create-or-update-activity-dialog.component'; +import { GfCreateOrUpdateActivityDialogComponent } from './create-or-update-activity-dialog/create-or-update-activity-dialog.component'; import { GfImportActivitiesDialogComponent } from './import-activities-dialog/import-activities-dialog.component'; import { ImportActivitiesDialogParams } from './import-activities-dialog/interfaces/interfaces'; @@ -306,15 +306,18 @@ export class GfActivitiesPageComponent implements OnDestroy, OnInit { } public openUpdateActivityDialog(aActivity: Activity) { - const dialogRef = this.dialog.open(GfCreateOrUpdateActivityDialog, { - data: { - activity: aActivity, - accounts: this.user?.accounts, - user: this.user - }, - height: this.deviceType === 'mobile' ? '98vh' : '80vh', - width: this.deviceType === 'mobile' ? '100vw' : '50rem' - }); + const dialogRef = this.dialog.open( + GfCreateOrUpdateActivityDialogComponent, + { + data: { + activity: aActivity, + accounts: this.user?.accounts, + user: this.user + }, + height: this.deviceType === 'mobile' ? '98vh' : '80vh', + width: this.deviceType === 'mobile' ? '100vw' : '50rem' + } + ); dialogRef .afterClosed() @@ -347,23 +350,26 @@ export class GfActivitiesPageComponent implements OnDestroy, OnInit { .subscribe((user) => { this.updateUser(user); - const dialogRef = this.dialog.open(GfCreateOrUpdateActivityDialog, { - data: { - accounts: this.user?.accounts, - activity: { - ...aActivity, - accountId: aActivity?.accountId, - date: new Date(), - id: null, - fee: 0, - type: aActivity?.type ?? 'BUY', - unitPrice: null + const dialogRef = this.dialog.open( + GfCreateOrUpdateActivityDialogComponent, + { + data: { + accounts: this.user?.accounts, + activity: { + ...aActivity, + accountId: aActivity?.accountId, + date: new Date(), + id: null, + fee: 0, + type: aActivity?.type ?? 'BUY', + unitPrice: null + }, + user: this.user }, - user: this.user - }, - height: this.deviceType === 'mobile' ? '98vh' : '80vh', - width: this.deviceType === 'mobile' ? '100vw' : '50rem' - }); + height: this.deviceType === 'mobile' ? '98vh' : '80vh', + width: this.deviceType === 'mobile' ? '100vw' : '50rem' + } + ); dialogRef .afterClosed() diff --git a/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.component.ts b/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.component.ts index 6454b9918..3261e9752 100644 --- a/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.component.ts +++ b/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.component.ts @@ -76,7 +76,7 @@ import { ActivityType } from './types/activity-type.type'; styleUrls: ['./create-or-update-activity-dialog.scss'], templateUrl: 'create-or-update-activity-dialog.html' }) -export class GfCreateOrUpdateActivityDialog implements OnDestroy { +export class GfCreateOrUpdateActivityDialogComponent implements OnDestroy { public activityForm: FormGroup; public assetClassOptions: AssetClassSelectorOption[] = Object.keys(AssetClass) @@ -110,7 +110,7 @@ export class GfCreateOrUpdateActivityDialog implements OnDestroy { @Inject(MAT_DIALOG_DATA) public data: CreateOrUpdateActivityDialogParams, private dataService: DataService, private dateAdapter: DateAdapter, - public dialogRef: MatDialogRef, + public dialogRef: MatDialogRef, private formBuilder: FormBuilder, @Inject(MAT_DATE_LOCALE) private locale: string, private userService: UserService From 0a79a27e0db729383aaec369b7bab626f36da803 Mon Sep 17 00:00:00 2001 From: SK Akram Date: Wed, 1 Oct 2025 18:03:36 +0530 Subject: [PATCH 04/24] Task/prefix home watchlist component with Gf (#5640) * Prefix home watchlist component with Gf --- .../app/components/home-watchlist/home-watchlist.component.ts | 2 +- apps/client/src/app/pages/home/home-page.routes.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/client/src/app/components/home-watchlist/home-watchlist.component.ts b/apps/client/src/app/components/home-watchlist/home-watchlist.component.ts index efe93a843..4c0b614c0 100644 --- a/apps/client/src/app/components/home-watchlist/home-watchlist.component.ts +++ b/apps/client/src/app/components/home-watchlist/home-watchlist.component.ts @@ -45,7 +45,7 @@ import { CreateWatchlistItemDialogParams } from './create-watchlist-item-dialog/ styleUrls: ['./home-watchlist.scss'], templateUrl: './home-watchlist.html' }) -export class HomeWatchlistComponent implements OnDestroy, OnInit { +export class GfHomeWatchlistComponent implements OnDestroy, OnInit { public deviceType: string; public hasImpersonationId: boolean; public hasPermissionToCreateWatchlistItem: boolean; diff --git a/apps/client/src/app/pages/home/home-page.routes.ts b/apps/client/src/app/pages/home/home-page.routes.ts index 9ae280939..452844cd7 100644 --- a/apps/client/src/app/pages/home/home-page.routes.ts +++ b/apps/client/src/app/pages/home/home-page.routes.ts @@ -2,7 +2,7 @@ import { GfHomeHoldingsComponent } from '@ghostfolio/client/components/home-hold import { HomeMarketComponent } from '@ghostfolio/client/components/home-market/home-market.component'; import { GfHomeOverviewComponent } from '@ghostfolio/client/components/home-overview/home-overview.component'; import { GfHomeSummaryComponent } from '@ghostfolio/client/components/home-summary/home-summary.component'; -import { HomeWatchlistComponent } from '@ghostfolio/client/components/home-watchlist/home-watchlist.component'; +import { GfHomeWatchlistComponent } from '@ghostfolio/client/components/home-watchlist/home-watchlist.component'; import { MarketsComponent } from '@ghostfolio/client/components/markets/markets.component'; import { AuthGuard } from '@ghostfolio/client/core/auth.guard'; import { internalRoutes } from '@ghostfolio/common/routes/routes'; @@ -41,7 +41,7 @@ export const routes: Routes = [ }, { path: internalRoutes.home.subRoutes.watchlist.path, - component: HomeWatchlistComponent, + component: GfHomeWatchlistComponent, title: internalRoutes.home.subRoutes.watchlist.title } ], From 8b7ad345061d0ec8c810db10e3ba555a66b824ff Mon Sep 17 00:00:00 2001 From: Flavien Lankai-Mills Date: Wed, 1 Oct 2025 14:36:15 +0200 Subject: [PATCH 05/24] Task/add component suffix to create or update access dialog component (#5621) * Add component suffix --- .../create-or-update-access-dialog.component.ts | 4 ++-- .../user-account-access/user-account-access.component.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.component.ts b/apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.component.ts index 1a4d3210a..067eb2d59 100644 --- a/apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.component.ts +++ b/apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.component.ts @@ -47,7 +47,7 @@ import { CreateOrUpdateAccessDialogParams } from './interfaces/interfaces'; styleUrls: ['./create-or-update-access-dialog.scss'], templateUrl: 'create-or-update-access-dialog.html' }) -export class GfCreateOrUpdateAccessDialog implements OnDestroy { +export class GfCreateOrUpdateAccessDialogComponent implements OnDestroy { public accessForm: FormGroup; private unsubscribeSubject = new Subject(); @@ -55,7 +55,7 @@ export class GfCreateOrUpdateAccessDialog implements OnDestroy { public constructor( private changeDetectorRef: ChangeDetectorRef, @Inject(MAT_DIALOG_DATA) private data: CreateOrUpdateAccessDialogParams, - public dialogRef: MatDialogRef, + public dialogRef: MatDialogRef, private dataService: DataService, private formBuilder: FormBuilder, private notificationService: NotificationService diff --git a/apps/client/src/app/components/user-account-access/user-account-access.component.ts b/apps/client/src/app/components/user-account-access/user-account-access.component.ts index c7959486b..178df374d 100644 --- a/apps/client/src/app/components/user-account-access/user-account-access.component.ts +++ b/apps/client/src/app/components/user-account-access/user-account-access.component.ts @@ -30,7 +30,7 @@ import { DeviceDetectorService } from 'ngx-device-detector'; import { EMPTY, Subject } from 'rxjs'; import { catchError, takeUntil } from 'rxjs/operators'; -import { GfCreateOrUpdateAccessDialog } from './create-or-update-access-dialog/create-or-update-access-dialog.component'; +import { GfCreateOrUpdateAccessDialogComponent } from './create-or-update-access-dialog/create-or-update-access-dialog.component'; @Component({ changeDetection: ChangeDetectionStrategy.OnPush, @@ -179,7 +179,7 @@ export class GfUserAccountAccessComponent implements OnDestroy, OnInit { } private openCreateAccessDialog() { - const dialogRef = this.dialog.open(GfCreateOrUpdateAccessDialog, { + const dialogRef = this.dialog.open(GfCreateOrUpdateAccessDialogComponent, { data: { access: { alias: '', From 3c1118e02722f84fcf1790a900a28220272797c3 Mon Sep 17 00:00:00 2001 From: Jasmeet Singh Date: Wed, 1 Oct 2025 18:08:57 +0530 Subject: [PATCH 06/24] Task/prefix home market component with Gf (#5643) * Prefix home market component with Gf --- .../src/app/components/home-market/home-market.component.ts | 2 +- apps/client/src/app/pages/home/home-page.routes.ts | 4 ++-- apps/client/src/app/pages/markets/markets-page.component.ts | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/client/src/app/components/home-market/home-market.component.ts b/apps/client/src/app/components/home-market/home-market.component.ts index f0aa4352d..32d89f8e0 100644 --- a/apps/client/src/app/components/home-market/home-market.component.ts +++ b/apps/client/src/app/components/home-market/home-market.component.ts @@ -35,7 +35,7 @@ import { takeUntil } from 'rxjs/operators'; styleUrls: ['./home-market.scss'], templateUrl: './home-market.html' }) -export class HomeMarketComponent implements OnDestroy, OnInit { +export class GfHomeMarketComponent implements OnDestroy, OnInit { public benchmarks: Benchmark[]; public deviceType: string; public fearAndGreedIndex: number; diff --git a/apps/client/src/app/pages/home/home-page.routes.ts b/apps/client/src/app/pages/home/home-page.routes.ts index 452844cd7..60f1b6f87 100644 --- a/apps/client/src/app/pages/home/home-page.routes.ts +++ b/apps/client/src/app/pages/home/home-page.routes.ts @@ -1,5 +1,5 @@ import { GfHomeHoldingsComponent } from '@ghostfolio/client/components/home-holdings/home-holdings.component'; -import { HomeMarketComponent } from '@ghostfolio/client/components/home-market/home-market.component'; +import { GfHomeMarketComponent } from '@ghostfolio/client/components/home-market/home-market.component'; import { GfHomeOverviewComponent } from '@ghostfolio/client/components/home-overview/home-overview.component'; import { GfHomeSummaryComponent } from '@ghostfolio/client/components/home-summary/home-summary.component'; import { GfHomeWatchlistComponent } from '@ghostfolio/client/components/home-watchlist/home-watchlist.component'; @@ -31,7 +31,7 @@ export const routes: Routes = [ }, { path: internalRoutes.home.subRoutes.markets.path, - component: HomeMarketComponent, + component: GfHomeMarketComponent, title: internalRoutes.home.subRoutes.markets.title }, { diff --git a/apps/client/src/app/pages/markets/markets-page.component.ts b/apps/client/src/app/pages/markets/markets-page.component.ts index 2d4a25876..11d4ebbde 100644 --- a/apps/client/src/app/pages/markets/markets-page.component.ts +++ b/apps/client/src/app/pages/markets/markets-page.component.ts @@ -1,4 +1,4 @@ -import { HomeMarketComponent } from '@ghostfolio/client/components/home-market/home-market.component'; +import { GfHomeMarketComponent } from '@ghostfolio/client/components/home-market/home-market.component'; import { CommonModule } from '@angular/common'; import { Component, OnDestroy } from '@angular/core'; @@ -6,7 +6,7 @@ import { Subject } from 'rxjs'; @Component({ host: { class: 'page' }, - imports: [CommonModule, HomeMarketComponent], + imports: [CommonModule, GfHomeMarketComponent], selector: 'gf-markets-page', styleUrls: ['./markets-page.scss'], templateUrl: './markets-page.html' From bb139bcfe6ceca3c2482e0c5fee893a9cfd6f712 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Wed, 1 Oct 2025 15:34:21 +0200 Subject: [PATCH 07/24] Feature/improve language localization for de 20250930 (#5638) * Update translations * Update changelog --- CHANGELOG.md | 6 ++++++ apps/client/src/locales/messages.de.xlf | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a0c866e2d..eca7c850c 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 + +- Improved the language localization for German (`de`) + ## 2.204.0 - 2025-09-30 ### Added diff --git a/apps/client/src/locales/messages.de.xlf b/apps/client/src/locales/messages.de.xlf index ef65bd96d..d5add9012 100644 --- a/apps/client/src/locales/messages.de.xlf +++ b/apps/client/src/locales/messages.de.xlf @@ -5393,7 +5393,7 @@ , - entnehmen, + entnehmen, apps/client/src/app/pages/portfolio/fire/fire-page.html 93 From e9c42598db6ecc2a5bbdc2e537e981d2d8a039bb Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Wed, 1 Oct 2025 20:05:35 +0200 Subject: [PATCH 08/24] Feature/improve support for mutual funds in FMP service (#5651) * Improve support for mutual funds in getAssetProfile() * Update changelog --- CHANGELOG.md | 1 + .../financial-modeling-prep.service.ts | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eca7c850c..8035eb99c 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 +- Improved the support for mutual funds in the _Financial Modeling Prep_ service (get asset profiles) - Improved the language localization for German (`de`) ## 2.204.0 - 2025-09-30 diff --git a/apps/api/src/services/data-provider/financial-modeling-prep/financial-modeling-prep.service.ts b/apps/api/src/services/data-provider/financial-modeling-prep/financial-modeling-prep.service.ts index 8f52fb779..e64416258 100644 --- a/apps/api/src/services/data-provider/financial-modeling-prep/financial-modeling-prep.service.ts +++ b/apps/api/src/services/data-provider/financial-modeling-prep/financial-modeling-prep.service.ts @@ -106,7 +106,10 @@ export class FinancialModelingPrepService implements DataProviderInterface { response.assetClass = assetClass; response.assetSubClass = assetSubClass; - if (assetSubClass === AssetSubClass.ETF) { + if ( + assetSubClass === AssetSubClass.ETF || + assetSubClass === AssetSubClass.MUTUALFUND + ) { const etfCountryWeightings = await fetch( `${this.getUrl({ version: 'stable' })}/etf/country-weightings?symbol=${symbol}&apikey=${this.apiKey}`, { From 3cbbbb7c1e9a3042492da4d49219b3b93b78c038 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Wed, 1 Oct 2025 20:06:44 +0200 Subject: [PATCH 09/24] Feature/upgrade prisma to version 6.16.3 (#5652) * Upgrade prisma to version 6.16.3 * Update changelog --- CHANGELOG.md | 1 + package-lock.json | 92 +++++++++++++++++++++++------------------------ package.json | 4 +-- 3 files changed, 49 insertions(+), 48 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8035eb99c..2881c6ac9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Improved the support for mutual funds in the _Financial Modeling Prep_ service (get asset profiles) - Improved the language localization for German (`de`) +- Upgraded `prisma` from version `6.16.1` to `6.16.3` ## 2.204.0 - 2025-09-30 diff --git a/package-lock.json b/package-lock.json index f9129a9d4..57e7d851e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -44,7 +44,7 @@ "@nestjs/schedule": "6.0.0", "@nestjs/serve-static": "5.0.3", "@openrouter/ai-sdk-provider": "0.7.2", - "@prisma/client": "6.16.1", + "@prisma/client": "6.16.3", "@simplewebauthn/browser": "13.1.0", "@simplewebauthn/server": "13.1.1", "@stripe/stripe-js": "7.9.0", @@ -149,7 +149,7 @@ "nx": "21.5.1", "prettier": "3.6.2", "prettier-plugin-organize-attributes": "1.0.0", - "prisma": "6.16.1", + "prisma": "6.16.3", "react": "18.2.0", "react-dom": "18.2.0", "replace-in-file": "8.3.0", @@ -11960,9 +11960,9 @@ "license": "MIT" }, "node_modules/@prisma/client": { - "version": "6.16.1", - "resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.16.1.tgz", - "integrity": "sha512-QaBCOY29lLAxEFFJgBPyW3WInCW52fJeQTmWx/h6YsP5u0bwuqP51aP0uhqFvhK9DaZPwvai/M4tSDYLVE9vRg==", + "version": "6.16.3", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.16.3.tgz", + "integrity": "sha512-JfNfAtXG+/lIopsvoZlZiH2k5yNx87mcTS4t9/S5oufM1nKdXYxOvpDC1XoTCFBa5cQh7uXnbMPsmZrwZY80xw==", "hasInstallScript": true, "license": "Apache-2.0", "engines": { @@ -11982,9 +11982,9 @@ } }, "node_modules/@prisma/config": { - "version": "6.16.1", - "resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.16.1.tgz", - "integrity": "sha512-sz3uxRPNL62QrJ0EYiujCFkIGZ3hg+9hgC1Ae1HjoYuj0BxCqHua4JNijYvYCrh9LlofZDZcRBX3tHBfLvAngA==", + "version": "6.16.3", + "resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.16.3.tgz", + "integrity": "sha512-VlsLnG4oOuKGGMToEeVaRhoTBZu5H3q51jTQXb/diRags3WV0+BQK5MolJTtP6G7COlzoXmWeS11rNBtvg+qFQ==", "devOptional": true, "license": "Apache-2.0", "dependencies": { @@ -11995,53 +11995,53 @@ } }, "node_modules/@prisma/debug": { - "version": "6.16.1", - "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.16.1.tgz", - "integrity": "sha512-RWv/VisW5vJE4cDRTuAHeVedtGoItXTnhuLHsSlJ9202QKz60uiXWywBlVcqXVq8bFeIZoCoWH+R1duZJPwqLw==", + "version": "6.16.3", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.16.3.tgz", + "integrity": "sha512-89DdqWtdKd7qoc9/qJCKLTazj3W3zPEiz0hc7HfZdpjzm21c7orOUB5oHWJsG+4KbV4cWU5pefq3CuDVYF9vgA==", "devOptional": true, "license": "Apache-2.0" }, "node_modules/@prisma/engines": { - "version": "6.16.1", - "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.16.1.tgz", - "integrity": "sha512-EOnEM5HlosPudBqbI+jipmaW/vQEaF0bKBo4gVkGabasINHR6RpC6h44fKZEqx4GD8CvH+einD2+b49DQrwrAg==", + "version": "6.16.3", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.16.3.tgz", + "integrity": "sha512-b+Rl4nzQDcoqe6RIpSHv8f5lLnwdDGvXhHjGDiokObguAAv/O1KaX1Oc69mBW/GFWKQpCkOraobLjU6s1h8HGg==", "devOptional": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "@prisma/debug": "6.16.1", - "@prisma/engines-version": "6.16.0-7.1c57fdcd7e44b29b9313256c76699e91c3ac3c43", - "@prisma/fetch-engine": "6.16.1", - "@prisma/get-platform": "6.16.1" + "@prisma/debug": "6.16.3", + "@prisma/engines-version": "6.16.1-1.bb420e667c1820a8c05a38023385f6cc7ef8e83a", + "@prisma/fetch-engine": "6.16.3", + "@prisma/get-platform": "6.16.3" } }, "node_modules/@prisma/engines-version": { - "version": "6.16.0-7.1c57fdcd7e44b29b9313256c76699e91c3ac3c43", - "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-6.16.0-7.1c57fdcd7e44b29b9313256c76699e91c3ac3c43.tgz", - "integrity": "sha512-ThvlDaKIVrnrv97ujNFDYiQbeMQpLa0O86HFA2mNoip4mtFqM7U5GSz2ie1i2xByZtvPztJlNRgPsXGeM/kqAA==", + "version": "6.16.1-1.bb420e667c1820a8c05a38023385f6cc7ef8e83a", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-6.16.1-1.bb420e667c1820a8c05a38023385f6cc7ef8e83a.tgz", + "integrity": "sha512-fftRmosBex48Ph1v2ll1FrPpirwtPZpNkE5CDCY1Lw2SD2ctyrLlVlHiuxDAAlALwWBOkPbAll4+EaqdGuMhJw==", "devOptional": true, "license": "Apache-2.0" }, "node_modules/@prisma/fetch-engine": { - "version": "6.16.1", - "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.16.1.tgz", - "integrity": "sha512-fl/PKQ8da5YTayw86WD3O9OmKJEM43gD3vANy2hS5S1CnfW2oPXk+Q03+gUWqcKK306QqhjjIHRFuTZ31WaosQ==", + "version": "6.16.3", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.16.3.tgz", + "integrity": "sha512-bUoRIkVaI+CCaVGrSfcKev0/Mk4ateubqWqGZvQ9uCqFv2ENwWIR3OeNuGin96nZn5+SkebcD7RGgKr/+mJelw==", "devOptional": true, "license": "Apache-2.0", "dependencies": { - "@prisma/debug": "6.16.1", - "@prisma/engines-version": "6.16.0-7.1c57fdcd7e44b29b9313256c76699e91c3ac3c43", - "@prisma/get-platform": "6.16.1" + "@prisma/debug": "6.16.3", + "@prisma/engines-version": "6.16.1-1.bb420e667c1820a8c05a38023385f6cc7ef8e83a", + "@prisma/get-platform": "6.16.3" } }, "node_modules/@prisma/get-platform": { - "version": "6.16.1", - "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.16.1.tgz", - "integrity": "sha512-kUfg4vagBG7dnaGRcGd1c0ytQFcDj2SUABiuveIpL3bthFdTLI6PJeLEia6Q8Dgh+WhPdo0N2q0Fzjk63XTyaA==", + "version": "6.16.3", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.16.3.tgz", + "integrity": "sha512-X1LxiFXinJ4iQehrodGp0f66Dv6cDL0GbRlcCoLtSu6f4Wi+hgo7eND/afIs5029GQLgNWKZ46vn8hjyXTsHLA==", "devOptional": true, "license": "Apache-2.0", "dependencies": { - "@prisma/debug": "6.16.1" + "@prisma/debug": "6.16.3" } }, "node_modules/@redis/client": { @@ -17285,9 +17285,9 @@ } }, "node_modules/c12/node_modules/jiti": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.5.1.tgz", - "integrity": "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", "devOptional": true, "license": "MIT", "bin": { @@ -33713,16 +33713,16 @@ } }, "node_modules/nypm": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.1.tgz", - "integrity": "sha512-hlacBiRiv1k9hZFiphPUkfSQ/ZfQzZDzC+8z0wL3lvDAOUu/2NnChkKuMoMjNur/9OpKuz2QsIeiPVN0xM5Q0w==", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.2.tgz", + "integrity": "sha512-7eM+hpOtrKrBDCh7Ypu2lJ9Z7PNZBdi/8AT3AX8xoCj43BBVHD0hPSTEvMtkMpfs8FCqBGhxB+uToIQimA111g==", "devOptional": true, "license": "MIT", "dependencies": { "citty": "^0.1.6", "consola": "^3.4.2", "pathe": "^2.0.3", - "pkg-types": "^2.2.0", + "pkg-types": "^2.3.0", "tinyexec": "^1.0.1" }, "bin": { @@ -34903,9 +34903,9 @@ } }, "node_modules/pkg-types": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.2.0.tgz", - "integrity": "sha512-2SM/GZGAEkPp3KWORxQZns4M+WSeXbC2HEvmOIJe3Cmiv6ieAJvdVhDldtHqM5J1Y7MrR1XhkBT/rMlhh9FdqQ==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz", + "integrity": "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==", "devOptional": true, "license": "MIT", "dependencies": { @@ -35747,15 +35747,15 @@ } }, "node_modules/prisma": { - "version": "6.16.1", - "resolved": "https://registry.npmjs.org/prisma/-/prisma-6.16.1.tgz", - "integrity": "sha512-MFkMU0eaDDKAT4R/By2IA9oQmwLTxokqv2wegAErr9Rf+oIe7W2sYpE/Uxq0H2DliIR7vnV63PkC1bEwUtl98w==", + "version": "6.16.3", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-6.16.3.tgz", + "integrity": "sha512-4tJq3KB9WRshH5+QmzOLV54YMkNlKOtLKaSdvraI5kC/axF47HuOw6zDM8xrxJ6s9o2WodY654On4XKkrobQdQ==", "devOptional": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "@prisma/config": "6.16.1", - "@prisma/engines": "6.16.1" + "@prisma/config": "6.16.3", + "@prisma/engines": "6.16.3" }, "bin": { "prisma": "build/index.js" diff --git a/package.json b/package.json index 6ad1e6498..a8aedcd6f 100644 --- a/package.json +++ b/package.json @@ -90,7 +90,7 @@ "@nestjs/schedule": "6.0.0", "@nestjs/serve-static": "5.0.3", "@openrouter/ai-sdk-provider": "0.7.2", - "@prisma/client": "6.16.1", + "@prisma/client": "6.16.3", "@simplewebauthn/browser": "13.1.0", "@simplewebauthn/server": "13.1.1", "@stripe/stripe-js": "7.9.0", @@ -195,7 +195,7 @@ "nx": "21.5.1", "prettier": "3.6.2", "prettier-plugin-organize-attributes": "1.0.0", - "prisma": "6.16.1", + "prisma": "6.16.3", "react": "18.2.0", "react-dom": "18.2.0", "replace-in-file": "8.3.0", From 244e63a127a762bd892236aac2c0636c4b70db27 Mon Sep 17 00:00:00 2001 From: Omar Eltomy <97570527+omareltomy@users.noreply.github.com> Date: Wed, 1 Oct 2025 21:09:20 +0300 Subject: [PATCH 10/24] Feature/restrict retirement date picker of FIRE calculator to future dates (#5627) * Restrict retirement date picker of FIRE calculator to future dates * Update changelog --- CHANGELOG.md | 1 + libs/ui/src/lib/fire-calculator/fire-calculator.component.html | 1 + libs/ui/src/lib/fire-calculator/fire-calculator.component.ts | 2 ++ 3 files changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2881c6ac9..d72c0bd84 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 +- Restricted the selection of the retirement date picker in the _FIRE_ calculator to a future date - Improved the support for mutual funds in the _Financial Modeling Prep_ service (get asset profiles) - Improved the language localization for German (`de`) - Upgraded `prisma` from version `6.16.1` to `6.16.3` diff --git a/libs/ui/src/lib/fire-calculator/fire-calculator.component.html b/libs/ui/src/lib/fire-calculator/fire-calculator.component.html index 60d5204e1..ef4722255 100644 --- a/libs/ui/src/lib/fire-calculator/fire-calculator.component.html +++ b/libs/ui/src/lib/fire-calculator/fire-calculator.component.html @@ -40,6 +40,7 @@ formControlName="retirementDate" matInput [matDatepicker]="datepicker" + [min]="minDate" /> ; public isLoading = true; + public minDate = addDays(new Date(), 1); public periodsToRetire = 0; private readonly CONTRIBUTION_PERIOD = 12; From 4f51253b7ec65eb5f41caeff7e8fe2e3a31ac138 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Wed, 1 Oct 2025 20:11:44 +0200 Subject: [PATCH 11/24] Release 2.205.0 (#5654) --- 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 d72c0bd84..717a36953 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.205.0 - 2025-10-01 ### Changed diff --git a/package-lock.json b/package-lock.json index 57e7d851e..8f8676556 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ghostfolio", - "version": "2.204.0", + "version": "2.205.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ghostfolio", - "version": "2.204.0", + "version": "2.205.0", "hasInstallScript": true, "license": "AGPL-3.0", "dependencies": { diff --git a/package.json b/package.json index a8aedcd6f..28881f546 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ghostfolio", - "version": "2.204.0", + "version": "2.205.0", "homepage": "https://ghostfol.io", "license": "AGPL-3.0", "repository": "https://github.com/ghostfolio/ghostfolio", From 4e2a1b8de5fb272036d79f88d3344573b3e774b5 Mon Sep 17 00:00:00 2001 From: Mariam Saeed <69825646+Mariam-Saeed@users.noreply.github.com> Date: Thu, 2 Oct 2025 16:17:02 +0300 Subject: [PATCH 12/24] Feature/improve usability of create watchlist item dialog (#5661) * Improve usability of create watchlist item dialog * Update changelog --- CHANGELOG.md | 6 ++++++ .../create-watchlist-item-dialog.html | 8 +++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 717a36953..6d0a3c3c0 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 + +- Improved the usability of the _Cancel_ / _Close_ buttons in the create watchlist item dialog + ## 2.205.0 - 2025-10-01 ### Changed diff --git a/apps/client/src/app/components/home-watchlist/create-watchlist-item-dialog/create-watchlist-item-dialog.html b/apps/client/src/app/components/home-watchlist/create-watchlist-item-dialog/create-watchlist-item-dialog.html index dd59a9309..92e194891 100644 --- a/apps/client/src/app/components/home-watchlist/create-watchlist-item-dialog/create-watchlist-item-dialog.html +++ b/apps/client/src/app/components/home-watchlist/create-watchlist-item-dialog/create-watchlist-item-dialog.html @@ -12,7 +12,13 @@
- +
-
+
Threshold Max: - @if (data.rule.configuration.threshold.unit === '%') { - {{ data.settings.thresholdMax | percent: '1.2-2' }} - } @else { - {{ data.settings.thresholdMax }} - } +
- @if (data.rule.configuration.threshold.unit === '%') { - - } @else { - - } + - @if (data.rule.configuration.threshold.unit === '%') { - - } @else { - - } +
} From 2248eb77f9ba7e1102ac068276e4841bd9cbab53 Mon Sep 17 00:00:00 2001 From: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Date: Sat, 4 Oct 2025 23:29:28 +0530 Subject: [PATCH 20/24] Feature/preselect first search result item in assistant (#5656) * Preselect first search result item in assistant * Update changelog --- CHANGELOG.md | 1 + .../src/lib/assistant/assistant.component.ts | 63 +++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a58a725c..008ec3055 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Localized the number formatting in the settings dialog to customize the rule thresholds of the _X-ray_ page +- Improved the usability of the assistant by preselecting the first search result - Improved the usability of the _Cancel_ / _Close_ buttons in the create watchlist item dialog - Refactored the `fireWealth` from `number` type to a structured object in the summary of the portfolio details endpoint - Refactored the _Open Startup_ (`/open`) page to standalone diff --git a/libs/ui/src/lib/assistant/assistant.component.ts b/libs/ui/src/lib/assistant/assistant.component.ts index 57c440bdb..3fc1cc232 100644 --- a/libs/ui/src/lib/assistant/assistant.component.ts +++ b/libs/ui/src/lib/assistant/assistant.component.ts @@ -169,6 +169,8 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { }; public tags: Filter[] = []; + private readonly PRESELECTION_DELAY = 100; + private filterTypes: Filter['type'][] = [ 'ACCOUNT', 'ASSET_CLASS', @@ -176,7 +178,9 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { 'SYMBOL', 'TAG' ]; + private keyManager: FocusKeyManager; + private preselectionTimeout: ReturnType; private unsubscribeSubject = new Subject(); public constructor( @@ -344,6 +348,9 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { .subscribe({ next: (searchResults) => { this.searchResults = searchResults; + + this.preselectFirstItem(); + this.changeDetectorRef.markForCheck(); }, error: (error) => { @@ -585,6 +592,10 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { } public ngOnDestroy() { + if (this.preselectionTimeout) { + clearTimeout(this.preselectionTimeout); + } + this.unsubscribeSubject.next(); this.unsubscribeSubject.complete(); } @@ -595,6 +606,58 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { }); } + private getFirstSearchResultItem() { + if (this.searchResults.quickLinks?.length > 0) { + return this.searchResults.quickLinks[0]; + } + + if (this.searchResults.accounts?.length > 0) { + return this.searchResults.accounts[0]; + } + + if (this.searchResults.holdings?.length > 0) { + return this.searchResults.holdings[0]; + } + + if (this.searchResults.assetProfiles?.length > 0) { + return this.searchResults.assetProfiles[0]; + } + + return null; + } + + private preselectFirstItem() { + if (this.preselectionTimeout) { + clearTimeout(this.preselectionTimeout); + } + + this.preselectionTimeout = setTimeout(() => { + if (!this.isOpen || !this.searchFormControl.value) { + return; + } + + const firstItem = this.getFirstSearchResultItem(); + + if (!firstItem) { + return; + } + + for (const item of this.assistantListItems) { + item.removeFocus(); + } + + this.keyManager.setFirstItemActive(); + + const currentFocusedItem = this.getCurrentAssistantListItem(); + + if (currentFocusedItem) { + currentFocusedItem.focus(); + } + + this.changeDetectorRef.markForCheck(); + }, this.PRESELECTION_DELAY); + } + private searchAccounts(aSearchTerm: string): Observable { return this.dataService .fetchAccounts({ From 6dc7edb7b57a57efc9465221d0751ed9100a9996 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Sat, 4 Oct 2025 20:11:48 +0200 Subject: [PATCH 21/24] Task/clean up unused import in holdings table component (#5682) * Clean up --- libs/ui/src/lib/holdings-table/holdings-table.component.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/libs/ui/src/lib/holdings-table/holdings-table.component.ts b/libs/ui/src/lib/holdings-table/holdings-table.component.ts index 83faf7621..1c46e18db 100644 --- a/libs/ui/src/lib/holdings-table/holdings-table.component.ts +++ b/libs/ui/src/lib/holdings-table/holdings-table.component.ts @@ -1,4 +1,3 @@ -import { GfSymbolPipe } from '@ghostfolio/client/pipes/symbol/symbol.pipe'; import { getLocale } from '@ghostfolio/common/helper'; import { AssetProfileIdentifier, @@ -34,7 +33,6 @@ import { GfValueComponent } from '../value/value.component'; imports: [ CommonModule, GfEntityLogoComponent, - GfSymbolPipe, GfValueComponent, MatButtonModule, MatDialogModule, From 400d4ae7b49bb5a78ec290a531cfc258be5a0731 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Sat, 4 Oct 2025 20:14:19 +0200 Subject: [PATCH 22/24] Release 2.206.0 (#5683) --- 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 008ec3055..5583fd59d 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.206.0 - 2025-10-04 ### Changed diff --git a/package-lock.json b/package-lock.json index 8f8676556..2ed25d7c2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ghostfolio", - "version": "2.205.0", + "version": "2.206.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ghostfolio", - "version": "2.205.0", + "version": "2.206.0", "hasInstallScript": true, "license": "AGPL-3.0", "dependencies": { diff --git a/package.json b/package.json index 28881f546..8717f58df 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ghostfolio", - "version": "2.205.0", + "version": "2.206.0", "homepage": "https://ghostfol.io", "license": "AGPL-3.0", "repository": "https://github.com/ghostfolio/ghostfolio", From 180e0f3c817d4f0fd607c8bc6fa60c49a6449787 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Sun, 5 Oct 2025 11:17:15 +0200 Subject: [PATCH 23/24] Feature/add date range query parameter to data gathering endpoint (#5684) * Add date range * Update changelog --- CHANGELOG.md | 6 ++++++ apps/api/src/app/admin/admin.controller.ts | 18 ++++++++++++++++-- .../data-gathering/data-gathering.service.ts | 14 +++++++++----- 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5583fd59d..fecc9758e 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 + +### Added + +- Added support for a date range query parameter in the data gathering endpoint + ## 2.206.0 - 2025-10-04 ### Changed diff --git a/apps/api/src/app/admin/admin.controller.ts b/apps/api/src/app/admin/admin.controller.ts index 27cc088d1..66f8483b4 100644 --- a/apps/api/src/app/admin/admin.controller.ts +++ b/apps/api/src/app/admin/admin.controller.ts @@ -6,6 +6,7 @@ import { ManualService } from '@ghostfolio/api/services/data-provider/manual/man import { DemoService } from '@ghostfolio/api/services/demo/demo.service'; import { PropertyDto } from '@ghostfolio/api/services/property/property.dto'; import { DataGatheringService } from '@ghostfolio/api/services/queues/data-gathering/data-gathering.service'; +import { getIntervalFromDateRange } from '@ghostfolio/common/calculation-helper'; import { DATA_GATHERING_QUEUE_PRIORITY_HIGH, DATA_GATHERING_QUEUE_PRIORITY_MEDIUM, @@ -22,6 +23,7 @@ import { } from '@ghostfolio/common/interfaces'; import { permissions } from '@ghostfolio/common/permissions'; import type { + DateRange, MarketDataPreset, RequestWithUser } from '@ghostfolio/common/types'; @@ -161,9 +163,21 @@ export class AdminController { @HasPermission(permissions.accessAdminControl) public async gatherSymbol( @Param('dataSource') dataSource: DataSource, - @Param('symbol') symbol: string + @Param('symbol') symbol: string, + @Query('range') dateRange: DateRange ): Promise { - this.dataGatheringService.gatherSymbol({ dataSource, symbol }); + let date: Date; + + if (dateRange) { + const { startDate } = getIntervalFromDateRange(dateRange, new Date()); + date = startDate; + } + + this.dataGatheringService.gatherSymbol({ + dataSource, + date, + symbol + }); return; } diff --git a/apps/api/src/services/queues/data-gathering/data-gathering.service.ts b/apps/api/src/services/queues/data-gathering/data-gathering.service.ts index 31edf6ffc..dd93e3e47 100644 --- a/apps/api/src/services/queues/data-gathering/data-gathering.service.ts +++ b/apps/api/src/services/queues/data-gathering/data-gathering.service.ts @@ -94,17 +94,21 @@ export class DataGatheringService { }); } - public async gatherSymbol({ dataSource, symbol }: AssetProfileIdentifier) { + public async gatherSymbol({ dataSource, date, symbol }: IDataGatheringItem) { await this.marketDataService.deleteMany({ dataSource, symbol }); - const dataGatheringItems = (await this.getSymbolsMax()).filter( - (dataGatheringItem) => { + const dataGatheringItems = (await this.getSymbolsMax()) + .filter((dataGatheringItem) => { return ( dataGatheringItem.dataSource === dataSource && dataGatheringItem.symbol === symbol ); - } - ); + }) + .map((item) => ({ + ...item, + date: date ?? item.date + })); + await this.gatherSymbols({ dataGatheringItems, priority: DATA_GATHERING_QUEUE_PRIORITY_HIGH From 5d65906722fa060f5b038005cd93e7ef4be7d8ae Mon Sep 17 00:00:00 2001 From: David Requeno <108202767+DavidReque@users.noreply.github.com> Date: Sun, 5 Oct 2025 08:44:23 -0600 Subject: [PATCH 24/24] Task/set up Storybook stories for activities table component (#5610) * Set up Storybook stories for activities table component * Update changelog --- CHANGELOG.md | 1 + .../activities-table.component.stories.ts | 471 ++++++++++++++++++ 2 files changed, 472 insertions(+) create mode 100644 libs/ui/src/lib/activities-table/activities-table.component.stories.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index fecc9758e..ecb75a2a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Added support for a date range query parameter in the data gathering endpoint +- Added a _Storybook_ story for the activities table component ## 2.206.0 - 2025-10-04 diff --git a/libs/ui/src/lib/activities-table/activities-table.component.stories.ts b/libs/ui/src/lib/activities-table/activities-table.component.stories.ts new file mode 100644 index 000000000..5e774730b --- /dev/null +++ b/libs/ui/src/lib/activities-table/activities-table.component.stories.ts @@ -0,0 +1,471 @@ +import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface'; +import { GfSymbolPipe } from '@ghostfolio/client/pipes/symbol/symbol.pipe'; + +import { CommonModule } from '@angular/common'; +import { MatButtonModule } from '@angular/material/button'; +import { MatCheckboxModule } from '@angular/material/checkbox'; +import { MatMenuModule } from '@angular/material/menu'; +import { MatPaginatorModule } from '@angular/material/paginator'; +import { MatSortModule } from '@angular/material/sort'; +import { MatTableDataSource, MatTableModule } from '@angular/material/table'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { RouterModule } from '@angular/router'; +import { IonIcon } from '@ionic/angular/standalone'; +import { moduleMetadata } from '@storybook/angular'; +import type { Meta, StoryObj } from '@storybook/angular'; +import { NotificationService } from 'apps/client/src/app/core/notification/notification.service'; +import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; + +import { GfActivityTypeComponent } from '../activity-type/activity-type.component'; +import { GfEntityLogoComponent } from '../entity-logo'; +import { GfNoTransactionsInfoComponent } from '../no-transactions-info/no-transactions-info.component'; +import { GfValueComponent } from '../value'; +import { GfActivitiesTableComponent } from './activities-table.component'; + +const activities: Activity[] = [ + { + accountId: '776bd1e9-b2f6-4f7e-933d-18756c2f0625', + accountUserId: '081aa387-487d-4438-83a4-3060eb2a016e', + comment: null, + createdAt: new Date('2025-04-09T13:47:33.133Z'), + currency: 'USD', + date: new Date('2025-04-09T13:45:45.504Z'), + fee: 1, + id: 'a76968ff-80a4-4453-81ed-c3627dea3919', + isDraft: false, + quantity: 115, + symbolProfileId: '21746431-d612-4298-911c-3099b2a43003', + type: 'BUY', + unitPrice: 103.543, + updatedAt: new Date('2025-05-31T18:43:01.840Z'), + userId: '081aa387-487d-4438-83a4-3060eb2a016e', + account: { + balance: 150.2, + comment: null, + createdAt: new Date('2025-05-31T13:00:13.940Z'), + currency: 'USD', + id: '776bd1e9-b2f6-4f7e-933d-18756c2f0625', + isExcluded: false, + name: 'Trading Account', + platformId: '9da3a8a7-4795-43e3-a6db-ccb914189737', + updatedAt: new Date('2025-06-01T06:53:10.569Z'), + userId: '081aa387-487d-4438-83a4-3060eb2a016e', + platform: { + id: '9da3a8a7-4795-43e3-a6db-ccb914189737', + name: 'Interactive Brokers', + url: 'https://interactivebrokers.com' + } + }, + SymbolProfile: { + assetClass: 'EQUITY', + assetSubClass: 'ETF', + comment: null, + countries: [], + createdAt: new Date('2021-06-06T16:12:20.982Z'), + currency: 'USD', + cusip: '922042742', + dataSource: 'YAHOO', + figi: 'BBG000GM5FZ6', + figiComposite: 'BBG000GM5FZ6', + figiShareClass: 'BBG001T2YZG9', + holdings: [], + id: '21746431-d612-4298-911c-3099b2a43003', + isActive: true, + isin: 'US9220427424', + name: 'Vanguard Total World Stock Index Fund ETF Shares', + updatedAt: new Date('2025-10-01T20:09:39.500Z'), + scraperConfiguration: null, + sectors: [], + symbol: 'VT', + symbolMapping: {}, + url: 'https://www.vanguard.com', + userId: null, + activitiesCount: 267, + dateOfFirstActivity: new Date('2018-05-31T16:00:00.000Z') + }, + tags: [], + feeInAssetProfileCurrency: 1, + feeInBaseCurrency: 1, + unitPriceInAssetProfileCurrency: 103.543, + value: 11907.445, + valueInBaseCurrency: 11907.445 + }, + { + accountId: '776bd1e9-b2f6-4f7e-933d-18756c2f0625', + accountUserId: '081aa387-487d-4438-83a4-3060eb2a016e', + comment: null, + createdAt: new Date('2024-08-07T13:40:39.103Z'), + currency: 'USD', + date: new Date('2024-08-07T13:38:06.289Z'), + fee: 2.97, + id: '0c2f4fbf-6edc-4adc-8f83-abf8148500ec', + isDraft: false, + quantity: 105, + symbolProfileId: '21746431-d612-4298-911c-3099b2a43003', + type: 'BUY', + unitPrice: 110.24, + updatedAt: new Date('2025-05-31T18:46:14.175Z'), + userId: '081aa387-487d-4438-83a4-3060eb2a016e', + account: { + balance: 150.2, + comment: null, + createdAt: new Date('2025-05-31T13:00:13.940Z'), + currency: 'USD', + id: '776bd1e9-b2f6-4f7e-933d-18756c2f0625', + isExcluded: false, + name: 'Trading Account', + platformId: '9da3a8a7-4795-43e3-a6db-ccb914189737', + updatedAt: new Date('2025-06-01T06:53:10.569Z'), + userId: '081aa387-487d-4438-83a4-3060eb2a016e', + platform: { + id: '9da3a8a7-4795-43e3-a6db-ccb914189737', + name: 'Interactive Brokers', + url: 'https://interactivebrokers.com' + } + }, + SymbolProfile: { + assetClass: 'EQUITY', + assetSubClass: 'ETF', + comment: null, + countries: [], + createdAt: new Date('2021-06-06T16:12:20.982Z'), + currency: 'USD', + cusip: '922042742', + dataSource: 'YAHOO', + figi: 'BBG000GM5FZ6', + figiComposite: 'BBG000GM5FZ6', + figiShareClass: 'BBG001T2YZG9', + holdings: [], + id: '21746431-d612-4298-911c-3099b2a43003', + isActive: true, + isin: 'US9220427424', + name: 'Vanguard Total World Stock Index Fund ETF Shares', + updatedAt: new Date('2025-10-01T20:09:39.500Z'), + scraperConfiguration: null, + sectors: [], + symbol: 'VT', + symbolMapping: {}, + url: 'https://www.vanguard.com', + userId: null, + activitiesCount: 267, + dateOfFirstActivity: new Date('2018-05-31T16:00:00.000Z') + }, + tags: [], + feeInAssetProfileCurrency: 2.97, + feeInBaseCurrency: 2.97, + unitPriceInAssetProfileCurrency: 110.24, + value: 11575.2, + valueInBaseCurrency: 11575.2 + }, + { + accountId: '776bd1e9-b2f6-4f7e-933d-18756c2f0625', + accountUserId: '081aa387-487d-4438-83a4-3060eb2a016e', + comment: null, + createdAt: new Date('2024-03-12T15:15:21.217Z'), + currency: 'USD', + date: new Date('2024-03-12T15:14:38.597Z'), + fee: 45.29, + id: 'bfc92677-faf4-4d4f-9762-e0ec056525c2', + isDraft: false, + quantity: 167, + symbolProfileId: '888d4123-db9a-42f3-9775-01b1ae6f9092', + type: 'BUY', + unitPrice: 41.0596, + updatedAt: new Date('2025-05-31T18:49:54.064Z'), + userId: '081aa387-487d-4438-83a4-3060eb2a016e', + account: { + balance: 150.2, + comment: null, + createdAt: new Date('2025-05-31T13:00:13.940Z'), + currency: 'USD', + id: '776bd1e9-b2f6-4f7e-933d-18756c2f0625', + isExcluded: false, + name: 'Trading Account', + platformId: '9da3a8a7-4795-43e3-a6db-ccb914189737', + updatedAt: new Date('2025-06-01T06:53:10.569Z'), + userId: '081aa387-487d-4438-83a4-3060eb2a016e', + platform: { + id: '9da3a8a7-4795-43e3-a6db-ccb914189737', + name: 'Interactive Brokers', + url: 'https://interactivebrokers.com' + } + }, + SymbolProfile: { + assetClass: 'LIQUIDITY', + assetSubClass: 'CRYPTOCURRENCY', + comment: null, + countries: [], + createdAt: new Date('2024-03-12T15:15:21.217Z'), + currency: 'USD', + cusip: '463918102', + dataSource: 'YAHOO', + figi: 'BBG01KYQ6PV3', + figiComposite: 'BBG01KYQ6PV3', + figiShareClass: 'BBG01KYQ6QS5', + holdings: [], + id: '888d4123-db9a-42f3-9775-01b1ae6f9092', + isActive: true, + isin: 'CA4639181029', + name: 'iShares Bitcoin Trust', + updatedAt: new Date('2025-09-29T03:14:07.742Z'), + scraperConfiguration: null, + sectors: [], + symbol: 'IBIT', + symbolMapping: {}, + url: 'https://www.ishares.com', + userId: null, + activitiesCount: 6, + dateOfFirstActivity: new Date('2024-01-01T08:00:00.000Z') + }, + tags: [], + feeInAssetProfileCurrency: 45.29, + feeInBaseCurrency: 45.29, + unitPriceInAssetProfileCurrency: 41.0596, + value: 6856.9532, + valueInBaseCurrency: 6856.9532 + }, + { + accountId: '776bd1e9-b2f6-4f7e-933d-18756c2f0625', + accountUserId: '081aa387-487d-4438-83a4-3060eb2a016e', + comment: null, + createdAt: new Date('2024-02-23T15:53:46.907Z'), + currency: 'USD', + date: new Date('2024-02-23T15:53:15.745Z'), + fee: 3, + id: '7c9ceb54-acb1-4850-bfb1-adb41c29fd6a', + isDraft: false, + quantity: 81, + symbolProfileId: '36effe43-7cb4-4e8b-b7ac-03ff65702cb9', + type: 'BUY', + unitPrice: 67.995, + updatedAt: new Date('2025-05-31T18:48:48.209Z'), + userId: '081aa387-487d-4438-83a4-3060eb2a016e', + account: { + balance: 150.2, + comment: null, + createdAt: new Date('2025-05-31T13:00:13.940Z'), + currency: 'USD', + id: '776bd1e9-b2f6-4f7e-933d-18756c2f0625', + isExcluded: false, + name: 'Trading Account', + platformId: '9da3a8a7-4795-43e3-a6db-ccb914189737', + updatedAt: new Date('2025-06-01T06:53:10.569Z'), + userId: '081aa387-487d-4438-83a4-3060eb2a016e', + platform: { + id: '9da3a8a7-4795-43e3-a6db-ccb914189737', + name: 'Interactive Brokers', + url: 'https://interactivebrokers.com' + } + }, + SymbolProfile: { + assetClass: 'FIXED_INCOME', + assetSubClass: 'BOND', + comment: 'No data', + countries: [], + createdAt: new Date('2022-04-13T20:05:47.301Z'), + currency: 'USD', + cusip: '92206C565', + dataSource: 'YAHOO', + figi: 'BBG00LWSF7T3', + figiComposite: 'BBG00LWSF7T3', + figiShareClass: 'BBG00LWSF8K0', + holdings: [], + id: '36effe43-7cb4-4e8b-b7ac-03ff65702cb9', + isActive: true, + isin: 'US92206C5655', + name: 'Vanguard Total World Bond ETF', + updatedAt: new Date('2025-10-02T06:02:56.314Z'), + + sectors: [], + symbol: 'BNDW', + symbolMapping: {}, + url: 'https://vanguard.com', + userId: null, + activitiesCount: 38, + dateOfFirstActivity: new Date('2022-04-13T20:05:48.742Z') + }, + tags: [], + feeInAssetProfileCurrency: 3, + feeInBaseCurrency: 3, + unitPriceInAssetProfileCurrency: 67.995, + value: 5507.595, + valueInBaseCurrency: 5507.595 + }, + { + accountId: '776bd1e9-b2f6-4f7e-933d-18756c2f0625', + accountUserId: '081aa387-487d-4438-83a4-3060eb2a016e', + comment: null, + createdAt: new Date('2023-01-11T14:35:22.325Z'), + currency: 'USD', + date: new Date('2023-01-11T14:34:55.174Z'), + fee: 7.38, + id: '3fe87b3f-78de-407a-bc02-4189b221051f', + isDraft: false, + quantity: 55, + symbolProfileId: '21746431-d612-4298-911c-3099b2a43003', + type: 'BUY', + unitPrice: 89.48, + updatedAt: new Date('2025-05-31T18:46:44.616Z'), + userId: '081aa387-487d-4438-83a4-3060eb2a016e', + account: { + balance: 150.2, + comment: null, + createdAt: new Date('2025-05-31T13:00:13.940Z'), + currency: 'USD', + id: '776bd1e9-b2f6-4f7e-933d-18756c2f0625', + isExcluded: false, + name: 'Trading Account', + platformId: '9da3a8a7-4795-43e3-a6db-ccb914189737', + updatedAt: new Date('2025-06-01T06:53:10.569Z'), + userId: '081aa387-487d-4438-83a4-3060eb2a016e', + platform: { + id: '9da3a8a7-4795-43e3-a6db-ccb914189737', + name: 'Interactive Brokers', + url: 'https://interactivebrokers.com' + } + }, + SymbolProfile: { + assetClass: 'EQUITY', + assetSubClass: 'ETF', + comment: null, + countries: [], + createdAt: new Date('2021-06-06T16:12:20.982Z'), + currency: 'USD', + cusip: '922042742', + dataSource: 'YAHOO', + figi: 'BBG000GM5FZ6', + figiComposite: 'BBG000GM5FZ6', + figiShareClass: 'BBG001T2YZG9', + holdings: [], + id: '21746431-d612-4298-911c-3099b2a43003', + isActive: true, + isin: 'US9220427424', + name: 'Vanguard Total World Stock Index Fund ETF Shares', + updatedAt: new Date('2025-10-01T20:09:39.500Z'), + scraperConfiguration: null, + sectors: [], + symbol: 'VT', + symbolMapping: {}, + url: 'https://www.vanguard.com', + userId: null, + activitiesCount: 267, + dateOfFirstActivity: new Date('2018-05-31T16:00:00.000Z') + }, + tags: [], + feeInAssetProfileCurrency: 7.38, + feeInBaseCurrency: 7.38, + unitPriceInAssetProfileCurrency: 89.48, + value: 4921.4, + valueInBaseCurrency: 4921.4 + } +]; + +const dataSource = new MatTableDataSource(activities); + +export default { + title: 'Activities Table', + component: GfActivitiesTableComponent, + decorators: [ + moduleMetadata({ + imports: [ + CommonModule, + GfActivityTypeComponent, + GfEntityLogoComponent, + GfNoTransactionsInfoComponent, + GfSymbolPipe, + GfValueComponent, + IonIcon, + MatButtonModule, + MatCheckboxModule, + MatMenuModule, + MatPaginatorModule, + MatSortModule, + MatTableModule, + MatTooltipModule, + NgxSkeletonLoaderModule, + RouterModule.forChild([]) + ], + providers: [NotificationService] + }) + ] +} as Meta; + +type Story = StoryObj; + +export const Loading: Story = { + args: { + baseCurrency: 'USD', + dataSource: undefined, + deviceType: 'desktop', + hasActivities: true, + hasPermissionToCreateActivity: false, + hasPermissionToDeleteActivity: false, + hasPermissionToExportActivities: false, + hasPermissionToOpenDetails: false, + locale: 'en-US', + pageIndex: 0, + pageSize: 10, + showAccountColumn: true, + showActions: false, + showCheckbox: false, + showNameColumn: true, + sortColumn: 'date', + sortDirection: 'desc', + sortDisabled: false, + totalItems: 0 + } +}; + +export const Default: Story = { + args: { + baseCurrency: 'USD', + dataSource, + deviceType: 'desktop', + hasActivities: true, + hasPermissionToCreateActivity: false, + hasPermissionToDeleteActivity: false, + hasPermissionToExportActivities: false, + hasPermissionToOpenDetails: false, + locale: 'en-US', + pageIndex: 0, + pageSize: 10, + showAccountColumn: true, + showActions: false, + showCheckbox: false, + showNameColumn: true, + sortColumn: 'date', + sortDirection: 'desc', + sortDisabled: false, + totalItems: activities.length + } +}; + +export const Pagination: Story = { + args: { + baseCurrency: 'USD', + dataSource: new MatTableDataSource( + Array.from({ length: 50 }).map((_, i) => ({ + ...(activities[i % activities.length] as Activity), + date: new Date(2025, 5, (i % 28) + 1), + id: `${i}` + })) + ), + deviceType: 'desktop', + hasActivities: true, + hasPermissionToCreateActivity: false, + hasPermissionToDeleteActivity: false, + hasPermissionToExportActivities: false, + hasPermissionToOpenDetails: false, + locale: 'en-US', + pageIndex: 0, + pageSize: 10, + showAccountColumn: true, + showActions: false, + showCheckbox: false, + showNameColumn: true, + sortColumn: 'date', + sortDirection: 'desc', + sortDisabled: false, + totalItems: 50 + } +};