Feature/add countries and sectors to position detail dialog (#692)
* Add asset and asset sub class * Add countries and sectors to position detail dialog * Update changelog
This commit is contained in:
@@ -11,7 +11,7 @@ import { DataService } from '@ghostfolio/client/services/data.service';
|
||||
import { DATE_FORMAT, downloadAsFile } from '@ghostfolio/common/helper';
|
||||
import { OrderWithAccount } from '@ghostfolio/common/types';
|
||||
import { LineChartItem } from '@ghostfolio/ui/line-chart/interfaces/line-chart.interface';
|
||||
import { AssetSubClass } from '@prisma/client';
|
||||
import { SymbolProfile } from '@prisma/client';
|
||||
import { format, isSameMonth, isToday, parseISO } from 'date-fns';
|
||||
import { Subject } from 'rxjs';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
@@ -26,10 +26,11 @@ import { PositionDetailDialogParams } from './interfaces/interfaces';
|
||||
styleUrls: ['./position-detail-dialog.component.scss']
|
||||
})
|
||||
export class PositionDetailDialog implements OnDestroy, OnInit {
|
||||
public assetSubClass: AssetSubClass;
|
||||
public averagePrice: number;
|
||||
public benchmarkDataItems: LineChartItem[];
|
||||
public currency: string;
|
||||
public countries: {
|
||||
[code: string]: { name: string; value: number };
|
||||
};
|
||||
public firstBuyDate: string;
|
||||
public grossPerformance: number;
|
||||
public grossPerformancePercent: number;
|
||||
@@ -38,13 +39,15 @@ export class PositionDetailDialog implements OnDestroy, OnInit {
|
||||
public marketPrice: number;
|
||||
public maxPrice: number;
|
||||
public minPrice: number;
|
||||
public name: string;
|
||||
public netPerformance: number;
|
||||
public netPerformancePercent: number;
|
||||
public orders: OrderWithAccount[];
|
||||
public quantity: number;
|
||||
public quantityPrecision = 2;
|
||||
public symbol: string;
|
||||
public sectors: {
|
||||
[name: string]: { name: string; value: number };
|
||||
};
|
||||
public SymbolProfile: SymbolProfile;
|
||||
public transactionCount: number;
|
||||
public value: number;
|
||||
|
||||
@@ -66,9 +69,7 @@ export class PositionDetailDialog implements OnDestroy, OnInit {
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
.subscribe(
|
||||
({
|
||||
assetSubClass,
|
||||
averagePrice,
|
||||
currency,
|
||||
firstBuyDate,
|
||||
grossPerformance,
|
||||
grossPerformancePercent,
|
||||
@@ -77,19 +78,17 @@ export class PositionDetailDialog implements OnDestroy, OnInit {
|
||||
marketPrice,
|
||||
maxPrice,
|
||||
minPrice,
|
||||
name,
|
||||
netPerformance,
|
||||
netPerformancePercent,
|
||||
orders,
|
||||
quantity,
|
||||
symbol,
|
||||
SymbolProfile,
|
||||
transactionCount,
|
||||
value
|
||||
}) => {
|
||||
this.assetSubClass = assetSubClass;
|
||||
this.averagePrice = averagePrice;
|
||||
this.benchmarkDataItems = [];
|
||||
this.currency = currency;
|
||||
this.countries = {};
|
||||
this.firstBuyDate = firstBuyDate;
|
||||
this.grossPerformance = grossPerformance;
|
||||
this.grossPerformancePercent = grossPerformancePercent;
|
||||
@@ -110,15 +109,33 @@ export class PositionDetailDialog implements OnDestroy, OnInit {
|
||||
this.marketPrice = marketPrice;
|
||||
this.maxPrice = maxPrice;
|
||||
this.minPrice = minPrice;
|
||||
this.name = name;
|
||||
this.netPerformance = netPerformance;
|
||||
this.netPerformancePercent = netPerformancePercent;
|
||||
this.orders = orders;
|
||||
this.quantity = quantity;
|
||||
this.symbol = symbol;
|
||||
this.sectors = {};
|
||||
this.SymbolProfile = SymbolProfile;
|
||||
this.transactionCount = transactionCount;
|
||||
this.value = value;
|
||||
|
||||
if (SymbolProfile?.countries?.length > 0) {
|
||||
for (const country of SymbolProfile.countries) {
|
||||
this.countries[country.code] = {
|
||||
name: country.name,
|
||||
value: country.weight
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (SymbolProfile?.sectors?.length > 0) {
|
||||
for (const sector of SymbolProfile.sectors) {
|
||||
this.sectors[sector.name] = {
|
||||
name: sector.name,
|
||||
value: sector.weight
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (isToday(parseISO(this.firstBuyDate))) {
|
||||
// Add average price
|
||||
this.historicalDataItems.push({
|
||||
@@ -166,7 +183,7 @@ export class PositionDetailDialog implements OnDestroy, OnInit {
|
||||
|
||||
if (Number.isInteger(this.quantity)) {
|
||||
this.quantityPrecision = 0;
|
||||
} else if (assetSubClass === 'CRYPTOCURRENCY') {
|
||||
} else if (this.SymbolProfile?.assetSubClass === 'CRYPTOCURRENCY') {
|
||||
if (this.quantity < 1) {
|
||||
this.quantityPrecision = 7;
|
||||
} else if (this.quantity < 1000) {
|
||||
@@ -196,7 +213,7 @@ export class PositionDetailDialog implements OnDestroy, OnInit {
|
||||
.subscribe((data) => {
|
||||
downloadAsFile(
|
||||
data,
|
||||
`ghostfolio-export-${this.symbol}-${format(
|
||||
`ghostfolio-export-${this.SymbolProfile?.symbol}-${format(
|
||||
parseISO(data.meta.date),
|
||||
'yyyyMMddHHmm'
|
||||
)}.json`,
|
||||
|
@@ -2,7 +2,7 @@
|
||||
mat-dialog-title
|
||||
position="center"
|
||||
[deviceType]="data.deviceType"
|
||||
[title]="name ?? symbol"
|
||||
[title]="SymbolProfile?.name ?? SymbolProfile?.symbol"
|
||||
(closeButtonClicked)="onClose()"
|
||||
></gf-dialog-header>
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
<gf-value
|
||||
label="Ø Buy Price"
|
||||
size="medium"
|
||||
[currency]="currency"
|
||||
[currency]="SymbolProfile?.currency"
|
||||
[locale]="data.locale"
|
||||
[value]="averagePrice"
|
||||
></gf-value>
|
||||
@@ -64,7 +64,7 @@
|
||||
<gf-value
|
||||
label="Market Price"
|
||||
size="medium"
|
||||
[currency]="currency"
|
||||
[currency]="SymbolProfile?.currency"
|
||||
[locale]="data.locale"
|
||||
[value]="marketPrice"
|
||||
></gf-value>
|
||||
@@ -73,7 +73,7 @@
|
||||
<gf-value
|
||||
label="Minimum Price"
|
||||
size="medium"
|
||||
[currency]="currency"
|
||||
[currency]="SymbolProfile?.currency"
|
||||
[locale]="data.locale"
|
||||
[ngClass]="{ 'text-danger': minPrice?.toFixed(2) === marketPrice?.toFixed(2) && maxPrice?.toFixed(2) !== minPrice?.toFixed(2) }"
|
||||
[value]="minPrice"
|
||||
@@ -83,7 +83,7 @@
|
||||
<gf-value
|
||||
label="Maximum Price"
|
||||
size="medium"
|
||||
[currency]="currency"
|
||||
[currency]="SymbolProfile?.currency"
|
||||
[locale]="data.locale"
|
||||
[ngClass]="{ 'text-success': maxPrice?.toFixed(2) === marketPrice?.toFixed(2) && maxPrice?.toFixed(2) !== minPrice?.toFixed(2) }"
|
||||
[value]="maxPrice"
|
||||
@@ -122,6 +122,72 @@
|
||||
[value]="transactionCount"
|
||||
></gf-value>
|
||||
</div>
|
||||
<div class="col-6 mb-3">
|
||||
<gf-value
|
||||
label="Asset Class"
|
||||
size="medium"
|
||||
[value]="SymbolProfile?.assetClass"
|
||||
></gf-value>
|
||||
</div>
|
||||
<div class="col-6 mb-3">
|
||||
<gf-value
|
||||
size="medium"
|
||||
label="Asset Sub Class"
|
||||
[locale]="data.locale"
|
||||
[value]="SymbolProfile?.assetSubClass"
|
||||
></gf-value>
|
||||
</div>
|
||||
<ng-container
|
||||
*ngIf="SymbolProfile?.countries?.length > 0 || SymbolProfile?.sectors?.length > 0"
|
||||
>
|
||||
<ng-container
|
||||
*ngIf="SymbolProfile?.countries?.length === 1 && SymbolProfile?.sectors?.length === 1; else charts"
|
||||
>
|
||||
<div
|
||||
*ngIf="SymbolProfile?.countries?.length === 1"
|
||||
class="col-6 mb-3"
|
||||
>
|
||||
<gf-value
|
||||
label="Country"
|
||||
size="medium"
|
||||
[locale]="data.locale"
|
||||
[value]="SymbolProfile.countries[0].name"
|
||||
></gf-value>
|
||||
</div>
|
||||
<div *ngIf="SymbolProfile?.sectors?.length === 1" class="col-6 mb-3">
|
||||
<gf-value
|
||||
label="Sector"
|
||||
size="medium"
|
||||
[locale]="data.locale"
|
||||
[value]="SymbolProfile.sectors[0].name"
|
||||
></gf-value>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-template #charts>
|
||||
<div class="col-6 mb-3">
|
||||
<div class="h4 mb-0" i18n>Countries</div>
|
||||
<gf-portfolio-proportion-chart
|
||||
[baseCurrency]="user?.settings?.baseCurrency"
|
||||
[isInPercent]="true"
|
||||
[keys]="['name']"
|
||||
[locale]="user?.settings?.locale"
|
||||
[maxItems]="10"
|
||||
[positions]="countries"
|
||||
></gf-portfolio-proportion-chart>
|
||||
</div>
|
||||
<div class="col-6 mb-3">
|
||||
<div class="h4 mb-0" i18n>Sectors</div>
|
||||
<gf-portfolio-proportion-chart
|
||||
[baseCurrency]="user?.settings?.baseCurrency"
|
||||
[isInPercent]="true"
|
||||
[keys]="['name']"
|
||||
[locale]="user?.settings?.locale"
|
||||
[maxItems]="10"
|
||||
[positions]="sectors"
|
||||
></gf-portfolio-proportion-chart>
|
||||
</div>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@@ -6,6 +6,7 @@ import { GfDialogFooterModule } from '@ghostfolio/client/components/dialog-foote
|
||||
import { GfDialogHeaderModule } from '@ghostfolio/client/components/dialog-header/dialog-header.module';
|
||||
import { GfActivitiesTableModule } from '@ghostfolio/ui/activities-table/activities-table.module';
|
||||
import { GfLineChartModule } from '@ghostfolio/ui/line-chart/line-chart.module';
|
||||
import { GfPortfolioProportionChartModule } from '@ghostfolio/ui/portfolio-proportion-chart/portfolio-proportion-chart.module';
|
||||
import { GfValueModule } from '@ghostfolio/ui/value';
|
||||
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
|
||||
|
||||
@@ -20,6 +21,7 @@ import { PositionDetailDialog } from './position-detail-dialog.component';
|
||||
GfDialogFooterModule,
|
||||
GfDialogHeaderModule,
|
||||
GfLineChartModule,
|
||||
GfPortfolioProportionChartModule,
|
||||
GfValueModule,
|
||||
MatButtonModule,
|
||||
MatDialogModule,
|
||||
|
Reference in New Issue
Block a user