Browse Source

Feature/add toggle to switch between open or closed top holdings in the analysis

Co-authored-by: Copilot <copilot@github.com>
pull/6972/head
swissbuechi 3 months ago
parent
commit
e9c915f406
  1. 1
      CHANGELOG.md
  2. 91
      apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts
  3. 21
      apps/client/src/app/pages/portfolio/analysis/analysis-page.html

1
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

91
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();
});
}
}

21
apps/client/src/app/pages/portfolio/analysis/analysis-page.html

@ -295,8 +295,25 @@
</div>
</div>
<div class="row align-items-center mb-4">
<div class="col-md-6 mb-3 mb-md-0">
<div class="align-items-center d-flex flex-grow-1 h5 mb-0 text-truncate">
<span i18n>Top and Bottom Holdings</span>
</div>
</div>
<div class="col-md-6 d-flex justify-content-md-end">
<gf-toggle
class="d-none d-lg-block"
[defaultValue]="holdingType"
[isLoading]="false"
[options]="holdingTypeOptions"
(valueChange)="onChangeHoldingType($event.value)"
/>
</div>
</div>
<div class="mb-5 row">
<div class="col-md-6 mb-3">
<div class="col-md-6 mb-3 mb-md-0">
<mat-card appearance="outlined" class="h-100">
<mat-card-header>
<mat-card-title class="align-items-center d-flex" i18n
@ -347,7 +364,7 @@
</mat-card-content>
</mat-card>
</div>
<div class="col-md-6 mb-3">
<div class="col-md-6">
<mat-card appearance="outlined" class="h-100">
<mat-card-header>
<mat-card-title class="align-items-center d-flex" i18n

Loading…
Cancel
Save