diff --git a/CHANGELOG.md b/CHANGELOG.md index d094f394b..a59193cd9 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 ### Added +- Added toggle to switch between open or closed top/bottom holdings in the analysis - Added support for routing selected requests through the _OpenRouter_ `web_fetch` tool in the `FetchService` ### Changed 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 03fd0767a..df803e0de 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 @@ -13,7 +13,11 @@ import { User } from '@ghostfolio/common/interfaces'; import { hasPermission, permissions } from '@ghostfolio/common/permissions'; -import type { AiPromptMode, GroupBy } from '@ghostfolio/common/types'; +import type { + AiPromptMode, + GroupBy, + HoldingType +} from '@ghostfolio/common/types'; import { translate } from '@ghostfolio/ui/i18n'; import { GfPremiumIndicatorComponent } from '@ghostfolio/ui/premium-indicator'; import { DataService } from '@ghostfolio/ui/services'; @@ -76,6 +80,11 @@ export class GfAnalysisPageComponent implements OnInit { public firstOrderDate: Date; public hasImpersonationId: boolean; public hasPermissionToReadAiPrompt: boolean; + public holdingType: HoldingType = 'ACTIVE'; + public holdingTypeOptions: ToggleOption[] = [ + { label: $localize`Open`, value: 'ACTIVE' }, + { label: $localize`Closed`, value: 'CLOSED' } + ]; public investments: InvestmentItem[]; public investmentTimelineDataLabel = $localize`Investment`; public investmentsByGroup: InvestmentItem[]; @@ -179,6 +188,11 @@ export class GfAnalysisPageComponent implements OnInit { this.fetchDividendsAndInvestments(); } + public onChangeHoldingType(aHoldingType: HoldingType) { + this.holdingType = aHoldingType; + this.updateTopAndBottomHoldings(); + } + public onCopyPromptToClipboard(mode: AiPromptMode) { if (mode === 'analysis') { this.isLoadingAnalysisPrompt = true; @@ -334,37 +348,7 @@ export class GfAnalysisPageComponent implements OnInit { this.changeDetectorRef.markForCheck(); }); - this.dataService - .fetchPortfolioHoldings({ - filters: this.userService.getFilters(), - range: this.user?.settings?.dateRange - }) - .pipe(takeUntilDestroyed(this.destroyRef)) - .subscribe(({ holdings }) => { - const holdingsSorted = sortBy( - holdings.filter(({ netPerformancePercentWithCurrencyEffect }) => { - return isNumber(netPerformancePercentWithCurrencyEffect); - }), - 'netPerformancePercentWithCurrencyEffect' - ).reverse(); - - this.top3 = holdingsSorted - .filter( - ({ netPerformancePercentWithCurrencyEffect }) => - netPerformancePercentWithCurrencyEffect > 0 - ) - .slice(0, 3); - - this.bottom3 = holdingsSorted - .filter( - ({ netPerformancePercentWithCurrencyEffect }) => - netPerformancePercentWithCurrencyEffect < 0 - ) - .slice(-3) - .reverse(); - - this.changeDetectorRef.markForCheck(); - }); + this.updateTopAndBottomHoldings(); this.fetchDividendsAndInvestments(); this.changeDetectorRef.markForCheck(); @@ -406,4 +390,47 @@ export class GfAnalysisPageComponent implements OnInit { } } } + + private updateTopAndBottomHoldings() { + const filters = this.userService.getFilters(); + + if (this.holdingType === 'CLOSED') { + filters.push({ id: 'CLOSED', type: 'HOLDING_TYPE' }); + } + + this.top3 = undefined; + this.bottom3 = undefined; + + this.dataService + .fetchPortfolioHoldings({ + filters, + range: this.user?.settings?.dateRange + }) + .pipe(takeUntilDestroyed(this.destroyRef)) + .subscribe(({ holdings }) => { + const holdingsSorted = sortBy( + holdings.filter(({ netPerformancePercentWithCurrencyEffect }) => { + return isNumber(netPerformancePercentWithCurrencyEffect); + }), + 'netPerformancePercentWithCurrencyEffect' + ).reverse(); + + this.top3 = holdingsSorted + .filter( + ({ netPerformancePercentWithCurrencyEffect }) => + netPerformancePercentWithCurrencyEffect > 0 + ) + .slice(0, 3); + + this.bottom3 = holdingsSorted + .filter( + ({ netPerformancePercentWithCurrencyEffect }) => + netPerformancePercentWithCurrencyEffect < 0 + ) + .slice(-3) + .reverse(); + + this.changeDetectorRef.markForCheck(); + }); + } } 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 b93be9182..33f5db479 100644 --- a/apps/client/src/app/pages/portfolio/analysis/analysis-page.html +++ b/apps/client/src/app/pages/portfolio/analysis/analysis-page.html @@ -295,8 +295,25 @@ +
+
+
+ Top and Bottom Holdings +
+
+
+ +
+
+
-
+
-
+