From 2199aad97b507a8f2da451e5bcec4658e8d7d571 Mon Sep 17 00:00:00 2001 From: felixjordandev Date: Tue, 17 Jun 2025 18:22:05 +0000 Subject: [PATCH 1/4] Refactor analysis page to simplify isInPercent condition by removing impersonation check --- .../src/app/pages/portfolio/analysis/analysis-page.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/client/src/app/pages/portfolio/analysis/analysis-page.html b/apps/client/src/app/pages/portfolio/analysis/analysis-page.html index 56c95e40f..ed9f8feb7 100644 --- a/apps/client/src/app/pages/portfolio/analysis/analysis-page.html +++ b/apps/client/src/app/pages/portfolio/analysis/analysis-page.html @@ -351,7 +351,7 @@ [benchmarkDataLabel]="portfolioEvolutionDataLabel" [currency]="user?.settings?.baseCurrency" [historicalDataItems]="performanceDataItems" - [isInPercent]="hasImpersonationId || user.settings.isRestrictedView" + [isInPercent]="user.settings.isRestrictedView" [isLoading]="isLoadingInvestmentChart" [locale]="user?.settings?.locale" /> @@ -407,7 +407,7 @@ [benchmarkDataLabel]="investmentTimelineDataLabel" [currency]="user?.settings?.baseCurrency" [groupBy]="mode" - [isInPercent]="hasImpersonationId || user.settings.isRestrictedView" + [isInPercent]="user.settings.isRestrictedView" [isLoading]="isLoadingInvestmentTimelineChart" [locale]="user?.settings?.locale" [savingsRate]="savingsRate" @@ -442,7 +442,7 @@ [benchmarkDataLabel]="dividendTimelineDataLabel" [currency]="user?.settings?.baseCurrency" [groupBy]="mode" - [isInPercent]="hasImpersonationId || user.settings.isRestrictedView" + [isInPercent]="user.settings.isRestrictedView" [isLoading]="isLoadingDividendTimelineChart" [locale]="user?.settings?.locale" /> From d0c36eb5ee29189929b886c324fac511545e1e41 Mon Sep 17 00:00:00 2001 From: felixjordandev Date: Wed, 25 Jun 2025 23:07:36 +0600 Subject: [PATCH 2/4] Enhance analysis page to incorporate restricted access checks for isInPercent condition and update related logic for user permissions --- .../analysis/analysis-page.component.ts | 23 +++++++++++++++++-- .../portfolio/analysis/analysis-page.html | 6 ++--- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts b/apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts index 2bd3096d4..367624b09 100644 --- a/apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts +++ b/apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts @@ -11,7 +11,7 @@ import { ToggleOption, User } from '@ghostfolio/common/interfaces'; -import { hasPermission, permissions } from '@ghostfolio/common/permissions'; +import { hasPermission, hasReadRestrictedAccessPermission, permissions } from '@ghostfolio/common/permissions'; import type { AiPromptMode, GroupBy } from '@ghostfolio/common/types'; import { translate } from '@ghostfolio/ui/i18n'; @@ -51,6 +51,7 @@ export class AnalysisPageComponent implements OnDestroy, OnInit { public dividendTimelineDataLabel = $localize`Dividend`; public firstOrderDate: Date; public hasImpersonationId: boolean; + public hasRestrictedAccess = false; public hasPermissionToReadAiPrompt: boolean; public investments: InvestmentItem[]; public investmentTimelineDataLabel = $localize`Investment`; @@ -93,7 +94,7 @@ export class AnalysisPageComponent implements OnDestroy, OnInit { get savingsRate() { const savingsRatePerMonth = - this.hasImpersonationId || this.user.settings.isRestrictedView + (this.hasImpersonationId && this.hasRestrictedAccess) || this.user.settings.isRestrictedView ? undefined : this.user?.settings?.savingsRate; @@ -110,6 +111,15 @@ export class AnalysisPageComponent implements OnDestroy, OnInit { .pipe(takeUntil(this.unsubscribeSubject)) .subscribe((impersonationId) => { this.hasImpersonationId = !!impersonationId; + + if (this.hasImpersonationId && this.user) { + this.hasRestrictedAccess = hasReadRestrictedAccessPermission({ + impersonationId, + user: this.user as any + }); + } else { + this.hasRestrictedAccess = false; + } }); this.userService.stateChanged @@ -118,6 +128,15 @@ export class AnalysisPageComponent implements OnDestroy, OnInit { if (state?.user) { this.user = state.user; + // Update hasRestrictedAccess when user changes + const impersonationId = this.impersonationStorageService.getId(); + if (impersonationId) { + this.hasRestrictedAccess = hasReadRestrictedAccessPermission({ + impersonationId, + user: this.user as any + }); + } + this.benchmark = this.benchmarks.find(({ id }) => { return id === this.user.settings?.benchmark; }); diff --git a/apps/client/src/app/pages/portfolio/analysis/analysis-page.html b/apps/client/src/app/pages/portfolio/analysis/analysis-page.html index ed9f8feb7..6acf5f569 100644 --- a/apps/client/src/app/pages/portfolio/analysis/analysis-page.html +++ b/apps/client/src/app/pages/portfolio/analysis/analysis-page.html @@ -351,7 +351,7 @@ [benchmarkDataLabel]="portfolioEvolutionDataLabel" [currency]="user?.settings?.baseCurrency" [historicalDataItems]="performanceDataItems" - [isInPercent]="user.settings.isRestrictedView" + [isInPercent]="user.settings.isRestrictedView || (hasImpersonationId && hasRestrictedAccess)" [isLoading]="isLoadingInvestmentChart" [locale]="user?.settings?.locale" /> @@ -407,7 +407,7 @@ [benchmarkDataLabel]="investmentTimelineDataLabel" [currency]="user?.settings?.baseCurrency" [groupBy]="mode" - [isInPercent]="user.settings.isRestrictedView" + [isInPercent]="user.settings.isRestrictedView || (hasImpersonationId && hasRestrictedAccess)" [isLoading]="isLoadingInvestmentTimelineChart" [locale]="user?.settings?.locale" [savingsRate]="savingsRate" @@ -442,7 +442,7 @@ [benchmarkDataLabel]="dividendTimelineDataLabel" [currency]="user?.settings?.baseCurrency" [groupBy]="mode" - [isInPercent]="user.settings.isRestrictedView" + [isInPercent]="user.settings.isRestrictedView || (hasImpersonationId && hasRestrictedAccess)" [isLoading]="isLoadingDividendTimelineChart" [locale]="user?.settings?.locale" /> From db9c468773004647c2c6b1ffecfcf7bcfc1adf4f Mon Sep 17 00:00:00 2001 From: felixjordandev Date: Wed, 25 Jun 2025 23:17:00 +0600 Subject: [PATCH 3/4] Implement user conversion to UserWithSettings format for permission checks in analysis page --- .../analysis/analysis-page.component.ts | 27 ++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts b/apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts index 367624b09..286bfecf4 100644 --- a/apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts +++ b/apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts @@ -12,7 +12,7 @@ import { User } from '@ghostfolio/common/interfaces'; import { hasPermission, hasReadRestrictedAccessPermission, permissions } from '@ghostfolio/common/permissions'; -import type { AiPromptMode, GroupBy } from '@ghostfolio/common/types'; +import type { AiPromptMode, GroupBy, UserWithSettings } from '@ghostfolio/common/types'; import { translate } from '@ghostfolio/ui/i18n'; import { Clipboard } from '@angular/cdk/clipboard'; @@ -103,6 +103,27 @@ export class AnalysisPageComponent implements OnDestroy, OnInit { : savingsRatePerMonth; } + // Helper method to convert User to UserWithSettings format required by hasReadRestrictedAccessPermission + private convertUserToUserWithSettings(user: User): UserWithSettings { + return { + ...user, + Access: user.access.map(access => ({ + ...access, + createdAt: new Date(), + updatedAt: new Date(), + granteeUserId: '', + userId: '' + })), + Settings: { + createdAt: new Date(), + updatedAt: new Date(), + id: user.id, + settings: user.settings, + userId: user.id + } + } as UserWithSettings; + } + public ngOnInit() { this.deviceType = this.deviceService.getDeviceInfo().deviceType; @@ -115,7 +136,7 @@ export class AnalysisPageComponent implements OnDestroy, OnInit { if (this.hasImpersonationId && this.user) { this.hasRestrictedAccess = hasReadRestrictedAccessPermission({ impersonationId, - user: this.user as any + user: this.convertUserToUserWithSettings(this.user) }); } else { this.hasRestrictedAccess = false; @@ -133,7 +154,7 @@ export class AnalysisPageComponent implements OnDestroy, OnInit { if (impersonationId) { this.hasRestrictedAccess = hasReadRestrictedAccessPermission({ impersonationId, - user: this.user as any + user: this.convertUserToUserWithSettings(this.user) }); } From b4f1159ff68013780346d1ad8a75ac72825d8326 Mon Sep 17 00:00:00 2001 From: felixjordandev Date: Wed, 25 Jun 2025 23:40:20 +0600 Subject: [PATCH 4/4] Refactor analysis page to remove restricted access checks and simplify permission logic --- .../analysis/analysis-page.component.ts | 46 ++----------------- .../portfolio/analysis/analysis-page.html | 6 +-- 2 files changed, 6 insertions(+), 46 deletions(-) diff --git a/apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts b/apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts index 286bfecf4..2bd3096d4 100644 --- a/apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts +++ b/apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts @@ -11,8 +11,8 @@ import { ToggleOption, User } from '@ghostfolio/common/interfaces'; -import { hasPermission, hasReadRestrictedAccessPermission, permissions } from '@ghostfolio/common/permissions'; -import type { AiPromptMode, GroupBy, UserWithSettings } from '@ghostfolio/common/types'; +import { hasPermission, permissions } from '@ghostfolio/common/permissions'; +import type { AiPromptMode, GroupBy } from '@ghostfolio/common/types'; import { translate } from '@ghostfolio/ui/i18n'; import { Clipboard } from '@angular/cdk/clipboard'; @@ -51,7 +51,6 @@ export class AnalysisPageComponent implements OnDestroy, OnInit { public dividendTimelineDataLabel = $localize`Dividend`; public firstOrderDate: Date; public hasImpersonationId: boolean; - public hasRestrictedAccess = false; public hasPermissionToReadAiPrompt: boolean; public investments: InvestmentItem[]; public investmentTimelineDataLabel = $localize`Investment`; @@ -94,7 +93,7 @@ export class AnalysisPageComponent implements OnDestroy, OnInit { get savingsRate() { const savingsRatePerMonth = - (this.hasImpersonationId && this.hasRestrictedAccess) || this.user.settings.isRestrictedView + this.hasImpersonationId || this.user.settings.isRestrictedView ? undefined : this.user?.settings?.savingsRate; @@ -103,27 +102,6 @@ export class AnalysisPageComponent implements OnDestroy, OnInit { : savingsRatePerMonth; } - // Helper method to convert User to UserWithSettings format required by hasReadRestrictedAccessPermission - private convertUserToUserWithSettings(user: User): UserWithSettings { - return { - ...user, - Access: user.access.map(access => ({ - ...access, - createdAt: new Date(), - updatedAt: new Date(), - granteeUserId: '', - userId: '' - })), - Settings: { - createdAt: new Date(), - updatedAt: new Date(), - id: user.id, - settings: user.settings, - userId: user.id - } - } as UserWithSettings; - } - public ngOnInit() { this.deviceType = this.deviceService.getDeviceInfo().deviceType; @@ -132,15 +110,6 @@ export class AnalysisPageComponent implements OnDestroy, OnInit { .pipe(takeUntil(this.unsubscribeSubject)) .subscribe((impersonationId) => { this.hasImpersonationId = !!impersonationId; - - if (this.hasImpersonationId && this.user) { - this.hasRestrictedAccess = hasReadRestrictedAccessPermission({ - impersonationId, - user: this.convertUserToUserWithSettings(this.user) - }); - } else { - this.hasRestrictedAccess = false; - } }); this.userService.stateChanged @@ -149,15 +118,6 @@ export class AnalysisPageComponent implements OnDestroy, OnInit { if (state?.user) { this.user = state.user; - // Update hasRestrictedAccess when user changes - const impersonationId = this.impersonationStorageService.getId(); - if (impersonationId) { - this.hasRestrictedAccess = hasReadRestrictedAccessPermission({ - impersonationId, - user: this.convertUserToUserWithSettings(this.user) - }); - } - this.benchmark = this.benchmarks.find(({ id }) => { return id === this.user.settings?.benchmark; }); diff --git a/apps/client/src/app/pages/portfolio/analysis/analysis-page.html b/apps/client/src/app/pages/portfolio/analysis/analysis-page.html index 6acf5f569..f318d0bb6 100644 --- a/apps/client/src/app/pages/portfolio/analysis/analysis-page.html +++ b/apps/client/src/app/pages/portfolio/analysis/analysis-page.html @@ -351,7 +351,7 @@ [benchmarkDataLabel]="portfolioEvolutionDataLabel" [currency]="user?.settings?.baseCurrency" [historicalDataItems]="performanceDataItems" - [isInPercent]="user.settings.isRestrictedView || (hasImpersonationId && hasRestrictedAccess)" + [isInPercent]="user.settings.isRestrictedView || hasImpersonationId" [isLoading]="isLoadingInvestmentChart" [locale]="user?.settings?.locale" /> @@ -407,7 +407,7 @@ [benchmarkDataLabel]="investmentTimelineDataLabel" [currency]="user?.settings?.baseCurrency" [groupBy]="mode" - [isInPercent]="user.settings.isRestrictedView || (hasImpersonationId && hasRestrictedAccess)" + [isInPercent]="user.settings.isRestrictedView || hasImpersonationId" [isLoading]="isLoadingInvestmentTimelineChart" [locale]="user?.settings?.locale" [savingsRate]="savingsRate" @@ -442,7 +442,7 @@ [benchmarkDataLabel]="dividendTimelineDataLabel" [currency]="user?.settings?.baseCurrency" [groupBy]="mode" - [isInPercent]="user.settings.isRestrictedView || (hasImpersonationId && hasRestrictedAccess)" + [isInPercent]="user.settings.isRestrictedView || hasImpersonationId" [isLoading]="isLoadingDividendTimelineChart" [locale]="user?.settings?.locale" />