From e668c6465eef44a2e66d8a23425514af49e6f425 Mon Sep 17 00:00:00 2001 From: Vahant Sharma Date: Tue, 30 Dec 2025 19:30:23 +0530 Subject: [PATCH 1/5] Bugfix/time in market for impersonation mode (#6103) * Fix time in market for impersonation mode * Update changelog --- CHANGELOG.md | 1 + apps/api/src/app/portfolio/portfolio.service.ts | 1 + .../portfolio-summary/portfolio-summary.component.ts | 11 +++++++---- apps/client/src/app/services/data.service.ts | 6 ++++++ .../src/lib/interfaces/portfolio-summary.interface.ts | 1 + 5 files changed, 16 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 843152af3..28bd529d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Added the missing currency suffix to the cash balance field in the create or update account dialog +- Fixed the time in market display of the portfolio summary tab on the home page for the impersonation mode - Fixed the delete button in the asset profile details dialog of the admin control panel by providing the missing `watchedByCount` parameter ## 2.224.2 - 2025-12-20 diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts index 084c8f4ed..faabee79b 100644 --- a/apps/api/src/app/portfolio/portfolio.service.ts +++ b/apps/api/src/app/portfolio/portfolio.service.ts @@ -1954,6 +1954,7 @@ export class PortfolioService { }).length, committedFunds: committedFunds.toNumber(), currentValueInBaseCurrency: currentValueInBaseCurrency.toNumber(), + dateOfFirstActivity: firstOrderDate, dividendInBaseCurrency: dividendInBaseCurrency.toNumber(), emergencyFund: { assets: emergencyFundHoldingsValueInBaseCurrency, diff --git a/apps/client/src/app/components/portfolio-summary/portfolio-summary.component.ts b/apps/client/src/app/components/portfolio-summary/portfolio-summary.component.ts index 9e9bb13d3..78ccce1ed 100644 --- a/apps/client/src/app/components/portfolio-summary/portfolio-summary.component.ts +++ b/apps/client/src/app/components/portfolio-summary/portfolio-summary.component.ts @@ -84,10 +84,13 @@ export class GfPortfolioSummaryComponent implements OnChanges { this.precision = 0; } - if (this.user.dateOfFirstActivity) { - this.timeInMarket = formatDistanceToNow(this.user.dateOfFirstActivity, { - locale: getDateFnsLocale(this.language) - }); + if (this.summary.dateOfFirstActivity) { + this.timeInMarket = formatDistanceToNow( + this.summary.dateOfFirstActivity, + { + locale: getDateFnsLocale(this.language) + } + ); } else { this.timeInMarket = '-'; } diff --git a/apps/client/src/app/services/data.service.ts b/apps/client/src/app/services/data.service.ts index 4c324fe03..31b0fef73 100644 --- a/apps/client/src/app/services/data.service.ts +++ b/apps/client/src/app/services/data.service.ts @@ -564,6 +564,12 @@ export class DataService { } } + if (response.summary?.dateOfFirstActivity) { + response.summary.dateOfFirstActivity = parseISO( + response.summary.dateOfFirstActivity + ); + } + return response; }) ); diff --git a/libs/common/src/lib/interfaces/portfolio-summary.interface.ts b/libs/common/src/lib/interfaces/portfolio-summary.interface.ts index f08eb61b8..ccf94dcf7 100644 --- a/libs/common/src/lib/interfaces/portfolio-summary.interface.ts +++ b/libs/common/src/lib/interfaces/portfolio-summary.interface.ts @@ -7,6 +7,7 @@ export interface PortfolioSummary extends PortfolioPerformance { annualizedPerformancePercentWithCurrencyEffect: number; cash: number; committedFunds: number; + dateOfFirstActivity: Date; dividendInBaseCurrency: number; emergencyFund: { assets: number; From 7abeb4d76ae0bc4eb1c54a105dadde9c76ace5b0 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Tue, 30 Dec 2025 15:04:46 +0100 Subject: [PATCH 2/5] Task/improve language localization for de 20251229 (#6108) * Update translations * Update changelog --- CHANGELOG.md | 1 + apps/client/src/locales/messages.de.xlf | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 28bd529d2..722c680a5 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 - Lifted the asset profile identifier editing restriction for `MANUAL` data sources in the asset profile details dialog of the admin control panel +- Improved the language localization for German (`de`) - Upgraded `angular` from version `20.2.4` to `20.3.9` - Upgraded `ng-extract-i18n-merge` from `3.1.0` to `3.2.1` - Upgraded `Nx` from version `21.5.1` to `22.1.3` diff --git a/apps/client/src/locales/messages.de.xlf b/apps/client/src/locales/messages.de.xlf index 7963e5841..4b405ce30 100644 --- a/apps/client/src/locales/messages.de.xlf +++ b/apps/client/src/locales/messages.de.xlf @@ -3475,7 +3475,7 @@ Upgrade Plan - Abonnement abschliessen + Mitgliedschaft abschliessen apps/client/src/app/components/header/header.component.html 193 @@ -3747,7 +3747,7 @@ Renew Plan - Abonnement erneuern + Mitgliedschaft erneuern apps/client/src/app/components/header/header.component.html 191 @@ -6934,7 +6934,7 @@ to use our referral link and get a Ghostfolio Premium membership for one year - um unseren Empfehlungslink zu verwenden und ein Ghostfolio Premium-Abonnement für ein Jahr zu erhalten + um unseren Empfehlungslink zu verwenden und eine Ghostfolio Premium-Mitgliedschaft für ein Jahr zu erhalten apps/client/src/app/pages/pricing/pricing-page.html 343 From ee1f606b5910f477033c45be80f717ffe16763ee Mon Sep 17 00:00:00 2001 From: Paul van der lei <54599584+0pilatos0@users.noreply.github.com> Date: Tue, 30 Dec 2025 15:13:55 +0100 Subject: [PATCH 3/5] Feature/create endpoint to get all platforms (#6097) * Create endpoint to get all platforms * Update changelog --- CHANGELOG.md | 4 ++++ apps/api/src/app/app.module.ts | 2 ++ .../platforms/platforms.controller.ts | 22 +++++++++++++++++++ .../endpoints/platforms/platforms.module.ts | 11 ++++++++++ libs/common/src/lib/interfaces/index.ts | 2 ++ .../responses/platforms-response.interface.ts | 5 +++++ libs/common/src/lib/permissions.ts | 2 ++ 7 files changed, 48 insertions(+) create mode 100644 apps/api/src/app/endpoints/platforms/platforms.controller.ts create mode 100644 apps/api/src/app/endpoints/platforms/platforms.module.ts create mode 100644 libs/common/src/lib/interfaces/responses/platforms-response.interface.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 722c680a5..7fe42255d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Added + +- Added a new endpoint to get all platforms (`GET api/v1/platforms`) + ### Changed - Lifted the asset profile identifier editing restriction for `MANUAL` data sources in the asset profile details dialog of the admin control panel diff --git a/apps/api/src/app/app.module.ts b/apps/api/src/app/app.module.ts index 5ec148558..89f52e1ea 100644 --- a/apps/api/src/app/app.module.ts +++ b/apps/api/src/app/app.module.ts @@ -37,6 +37,7 @@ import { AssetsModule } from './endpoints/assets/assets.module'; import { BenchmarksModule } from './endpoints/benchmarks/benchmarks.module'; import { GhostfolioModule } from './endpoints/data-providers/ghostfolio/ghostfolio.module'; import { MarketDataModule } from './endpoints/market-data/market-data.module'; +import { PlatformsModule } from './endpoints/platforms/platforms.module'; import { PublicModule } from './endpoints/public/public.module'; import { SitemapModule } from './endpoints/sitemap/sitemap.module'; import { TagsModule } from './endpoints/tags/tags.module'; @@ -95,6 +96,7 @@ import { UserModule } from './user/user.module'; MarketDataModule, OrderModule, PlatformModule, + PlatformsModule, PortfolioModule, PortfolioSnapshotQueueModule, PrismaModule, diff --git a/apps/api/src/app/endpoints/platforms/platforms.controller.ts b/apps/api/src/app/endpoints/platforms/platforms.controller.ts new file mode 100644 index 000000000..46303a3f8 --- /dev/null +++ b/apps/api/src/app/endpoints/platforms/platforms.controller.ts @@ -0,0 +1,22 @@ +import { PlatformService } from '@ghostfolio/api/app/platform/platform.service'; +import { HasPermission } from '@ghostfolio/api/decorators/has-permission.decorator'; +import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard'; +import { PlatformsResponse } from '@ghostfolio/common/interfaces'; +import { permissions } from '@ghostfolio/common/permissions'; + +import { Controller, Get, UseGuards } from '@nestjs/common'; +import { AuthGuard } from '@nestjs/passport'; + +@Controller('platforms') +export class PlatformsController { + public constructor(private readonly platformService: PlatformService) {} + + @Get() + @HasPermission(permissions.readPlatforms) + @UseGuards(AuthGuard('jwt'), HasPermissionGuard) + public async getPlatforms(): Promise { + const platforms = await this.platformService.getPlatforms(); + + return { platforms }; + } +} diff --git a/apps/api/src/app/endpoints/platforms/platforms.module.ts b/apps/api/src/app/endpoints/platforms/platforms.module.ts new file mode 100644 index 000000000..21d0edf69 --- /dev/null +++ b/apps/api/src/app/endpoints/platforms/platforms.module.ts @@ -0,0 +1,11 @@ +import { PlatformModule } from '@ghostfolio/api/app/platform/platform.module'; + +import { Module } from '@nestjs/common'; + +import { PlatformsController } from './platforms.controller'; + +@Module({ + controllers: [PlatformsController], + imports: [PlatformModule] +}) +export class PlatformsModule {} diff --git a/libs/common/src/lib/interfaces/index.ts b/libs/common/src/lib/interfaces/index.ts index 4b8e8009a..7cf93691c 100644 --- a/libs/common/src/lib/interfaces/index.ts +++ b/libs/common/src/lib/interfaces/index.ts @@ -68,6 +68,7 @@ import type { LookupResponse } from './responses/lookup-response.interface'; import type { MarketDataDetailsResponse } from './responses/market-data-details-response.interface'; import type { MarketDataOfMarketsResponse } from './responses/market-data-of-markets-response.interface'; import type { OAuthResponse } from './responses/oauth-response.interface'; +import type { PlatformsResponse } from './responses/platforms-response.interface'; import type { PortfolioDividendsResponse } from './responses/portfolio-dividends-response.interface'; import type { PortfolioHoldingResponse } from './responses/portfolio-holding-response.interface'; import type { PortfolioHoldingsResponse } from './responses/portfolio-holdings-response.interface'; @@ -158,6 +159,7 @@ export { MarketDataDetailsResponse, MarketDataOfMarketsResponse, OAuthResponse, + PlatformsResponse, PortfolioChart, PortfolioDetails, PortfolioDividendsResponse, diff --git a/libs/common/src/lib/interfaces/responses/platforms-response.interface.ts b/libs/common/src/lib/interfaces/responses/platforms-response.interface.ts new file mode 100644 index 000000000..552912d9d --- /dev/null +++ b/libs/common/src/lib/interfaces/responses/platforms-response.interface.ts @@ -0,0 +1,5 @@ +import { Platform } from '@prisma/client'; + +export interface PlatformsResponse { + platforms: Platform[]; +} diff --git a/libs/common/src/lib/permissions.ts b/libs/common/src/lib/permissions.ts index 3fd2bef8c..cb4eb175b 100644 --- a/libs/common/src/lib/permissions.ts +++ b/libs/common/src/lib/permissions.ts @@ -93,6 +93,7 @@ export function getPermissions(aRole: Role): string[] { permissions.readAiPrompt, permissions.readMarketData, permissions.readMarketDataOfOwnAssetProfile, + permissions.readPlatforms, permissions.readPlatformsWithAccountCount, permissions.readTags, permissions.readWatchlist, @@ -136,6 +137,7 @@ export function getPermissions(aRole: Role): string[] { permissions.deleteWatchlistItem, permissions.readAiPrompt, permissions.readMarketDataOfOwnAssetProfile, + permissions.readPlatforms, permissions.readWatchlist, permissions.updateAccount, permissions.updateAccess, From eb9d66e760724b33288667b010a7be75e85fd587 Mon Sep 17 00:00:00 2001 From: Karel De Smet Date: Tue, 30 Dec 2025 16:03:29 +0100 Subject: [PATCH 4/5] Task/improve user detail dialog routing in Admin Control panel (#6104) * Improve user detail dialog routing in Admin Control panel * Update changelog --- CHANGELOG.md | 1 + .../admin-users/admin-users.component.ts | 26 ++++++++++++------- .../components/admin-users/admin-users.html | 6 ++--- .../src/app/pages/admin/admin-page.routes.ts | 5 ++++ 4 files changed, 26 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7fe42255d..3eac30a5c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Improved the routing of the user detail dialog in the users section of the admin control panel - Lifted the asset profile identifier editing restriction for `MANUAL` data sources in the asset profile details dialog of the admin control panel - Improved the language localization for German (`de`) - Upgraded `angular` from version `20.2.4` to `20.3.9` diff --git a/apps/client/src/app/components/admin-users/admin-users.component.ts b/apps/client/src/app/components/admin-users/admin-users.component.ts index 99fbe7901..1722b498f 100644 --- a/apps/client/src/app/components/admin-users/admin-users.component.ts +++ b/apps/client/src/app/components/admin-users/admin-users.component.ts @@ -18,6 +18,7 @@ import { User } from '@ghostfolio/common/interfaces'; import { hasPermission, permissions } from '@ghostfolio/common/permissions'; +import { internalRoutes } from '@ghostfolio/common/routes/routes'; import { NotificationService } from '@ghostfolio/ui/notifications'; import { GfPremiumIndicatorComponent } from '@ghostfolio/ui/premium-indicator'; import { GfValueComponent } from '@ghostfolio/ui/value'; @@ -39,7 +40,7 @@ import { PageEvent } from '@angular/material/paginator'; import { MatTableDataSource, MatTableModule } from '@angular/material/table'; -import { ActivatedRoute, Router } from '@angular/router'; +import { ActivatedRoute, Router, RouterModule } from '@angular/router'; import { IonIcon } from '@ionic/angular/standalone'; import { differenceInSeconds, @@ -69,7 +70,8 @@ import { takeUntil } from 'rxjs/operators'; MatMenuModule, MatPaginatorModule, MatTableModule, - NgxSkeletonLoaderModule + NgxSkeletonLoaderModule, + RouterModule ], selector: 'gf-admin-users', styleUrls: ['./admin-users.scss'], @@ -88,6 +90,8 @@ export class GfAdminUsersComponent implements OnDestroy, OnInit { public info: InfoItem; public isLoading = false; public pageSize = DEFAULT_PAGE_SIZE; + public routerLinkAdminControlUsers = + internalRoutes.adminControl.subRoutes.users.routerLink; public totalItems = 0; public user: User; @@ -136,11 +140,13 @@ export class GfAdminUsersComponent implements OnDestroy, OnInit { ]; } - this.route.queryParams + this.route.paramMap .pipe(takeUntil(this.unsubscribeSubject)) .subscribe((params) => { - if (params['userDetailDialog'] && params['userId']) { - this.openUserDetailDialog(params['userId']); + const userId = params.get('userId'); + + if (userId) { + this.openUserDetailDialog(userId); } }); @@ -248,9 +254,9 @@ export class GfAdminUsersComponent implements OnDestroy, OnInit { } public onOpenUserDetailDialog(userId: string) { - this.router.navigate([], { - queryParams: { userId, userDetailDialog: true } - }); + this.router.navigate( + internalRoutes.adminControl.subRoutes.users.routerLink.concat(userId) + ); } public ngOnDestroy() { @@ -301,7 +307,9 @@ export class GfAdminUsersComponent implements OnDestroy, OnInit { .afterClosed() .pipe(takeUntil(this.unsubscribeSubject)) .subscribe(() => { - this.router.navigate(['.'], { relativeTo: this.route }); + this.router.navigate( + internalRoutes.adminControl.subRoutes.users.routerLink + ); }); } } diff --git a/apps/client/src/app/components/admin-users/admin-users.html b/apps/client/src/app/components/admin-users/admin-users.html index ebcdc6f5f..0f9789feb 100644 --- a/apps/client/src/app/components/admin-users/admin-users.html +++ b/apps/client/src/app/components/admin-users/admin-users.html @@ -215,9 +215,9 @@ - + @if (hasPermissionToImpersonateAllUsers) {