From ab379f9abfe60e9631a452ac3ad27cb66b39a65e Mon Sep 17 00:00:00 2001
From: Amandee Ellawala <47607256+amandee27@users.noreply.github.com>
Date: Wed, 12 Feb 2025 20:21:31 +0000
Subject: [PATCH] Feature/extend holding detail dialog by historical market
data editor (#4281)
* Extend holding detail dialog by historical market data editor
* Update changelog
---
CHANGELOG.md | 1 +
apps/api/src/app/user/user.service.ts | 5 +-
.../holding-detail-dialog.component.ts | 48 ++++++++++++++++++-
.../holding-detail-dialog.html | 21 ++++++++
...storical-market-data-editor.component.html | 1 +
5 files changed, 74 insertions(+), 2 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9e65772d..32623c52 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
- Added a new static portfolio analysis rule: _Regional Market Cluster Risk_ (Japan)
- Extended the tags selector component by a `readonly` attribute
- Extended the tags selector component to support creating custom tags
+- Extended the holding detail dialog by the historical market data editor (experimental)
- Added global styles to the _Storybook_ setup
### Changed
diff --git a/apps/api/src/app/user/user.service.ts b/apps/api/src/app/user/user.service.ts
index 7665e43d..30d10c8d 100644
--- a/apps/api/src/app/user/user.service.ts
+++ b/apps/api/src/app/user/user.service.ts
@@ -346,7 +346,10 @@ export class UserService {
currentPermissions,
permissions.accessHoldingsChart,
permissions.createAccess,
- permissions.readAiPrompt
+ permissions.createMarketDataOfOwnAssetProfile,
+ permissions.readAiPrompt,
+ permissions.readMarketDataOfOwnAssetProfile,
+ permissions.updateMarketDataOfOwnAssetProfile
);
// Reset benchmark
diff --git a/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts b/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts
index 297a990e..4f7e4efd 100644
--- a/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts
+++ b/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts
@@ -13,8 +13,10 @@ import {
LineChartItem,
User
} from '@ghostfolio/common/interfaces';
+import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import { GfActivitiesTableComponent } from '@ghostfolio/ui/activities-table';
import { GfDataProviderCreditsComponent } from '@ghostfolio/ui/data-provider-credits';
+import { GfHistoricalMarketDataEditorComponent } from '@ghostfolio/ui/historical-market-data-editor';
import { translate } from '@ghostfolio/ui/i18n';
import { GfLineChartComponent } from '@ghostfolio/ui/line-chart';
import { GfPortfolioProportionChartComponent } from '@ghostfolio/ui/portfolio-proportion-chart';
@@ -44,7 +46,7 @@ import { SortDirection } from '@angular/material/sort';
import { MatTableDataSource } from '@angular/material/table';
import { MatTabsModule } from '@angular/material/tabs';
import { Router } from '@angular/router';
-import { Account, Tag } from '@prisma/client';
+import { Account, MarketData, Tag } from '@prisma/client';
import { format, isSameMonth, isToday, parseISO } from 'date-fns';
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
import { Subject } from 'rxjs';
@@ -62,6 +64,7 @@ import { HoldingDetailDialogParams } from './interfaces/interfaces';
GfDataProviderCreditsComponent,
GfDialogFooterModule,
GfDialogHeaderModule,
+ GfHistoricalMarketDataEditorComponent,
GfLineChartComponent,
GfPortfolioProportionChartComponent,
GfTagsSelectorComponent,
@@ -95,9 +98,11 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit {
public dividendYieldPercentWithCurrencyEffect: number;
public feeInBaseCurrency: number;
public firstBuyDate: string;
+ public hasPermissionToReadMarketDataOfOwnAssetProfile: boolean;
public historicalDataItems: LineChartItem[];
public investment: number;
public investmentPrecision = 2;
+ public marketDataItems: MarketData[] = [];
public marketPrice: number;
public maxPrice: number;
public minPrice: number;
@@ -231,6 +236,14 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit {
this.feeInBaseCurrency = feeInBaseCurrency;
this.firstBuyDate = firstBuyDate;
+ this.hasPermissionToReadMarketDataOfOwnAssetProfile =
+ hasPermission(
+ this.user?.permissions,
+ permissions.readMarketDataOfOwnAssetProfile
+ ) &&
+ SymbolProfile?.dataSource === 'MANUAL' &&
+ SymbolProfile?.userId === this.user?.id;
+
this.historicalDataItems = historicalData.map(
({ averagePrice, date, marketPrice }) => {
this.benchmarkDataItems.push({
@@ -393,6 +406,10 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit {
}
);
+ if (this.hasPermissionToReadMarketDataOfOwnAssetProfile) {
+ this.fetchMarketData();
+ }
+
this.changeDetectorRef.markForCheck();
}
);
@@ -448,6 +465,12 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit {
});
}
+ public onMarketDataChanged(withRefresh = false) {
+ if (withRefresh) {
+ this.fetchMarketData();
+ }
+ }
+
public onTagsChanged(tags: Tag[]) {
this.activityForm.get('tags').setValue(tags);
}
@@ -464,4 +487,27 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit {
this.unsubscribeSubject.next();
this.unsubscribeSubject.complete();
}
+
+ private fetchMarketData() {
+ this.dataService
+ .fetchMarketDataBySymbol({
+ dataSource: this.data.dataSource,
+ symbol: this.data.symbol
+ })
+ .pipe(takeUntil(this.unsubscribeSubject))
+ .subscribe(({ marketData }) => {
+ this.marketDataItems = marketData;
+
+ this.historicalDataItems = this.marketDataItems.map(
+ ({ date, marketPrice }) => {
+ return {
+ date: format(date, DATE_FORMAT),
+ value: marketPrice
+ };
+ }
+ );
+
+ this.changeDetectorRef.markForCheck();
+ });
+ }
}
diff --git a/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html b/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html
index d820ddf7..e7b3cc1b 100644
--- a/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html
+++ b/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html
@@ -364,6 +364,27 @@
[showValueInBaseCurrency]="false"
/>
+ @if (
+ hasPermissionToReadMarketDataOfOwnAssetProfile &&
+ user?.settings?.isExperimentalFeatures
+ ) {
+
+
+
+ Market Data
+
+
+
+ }