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:
parent
9344dcd26e
commit
fa66cd5bce
@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Added the asset and asset sub class to the position detail dialog
|
||||||
|
- Added the countries and sectors to the position detail dialog
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Upgraded `angular` from version `13.1.2` to `13.2.3`
|
- Upgraded `angular` from version `13.1.2` to `13.2.3`
|
||||||
|
@ -1,11 +1,8 @@
|
|||||||
|
import { EnhancedSymbolProfile } from '@ghostfolio/api/services/interfaces/symbol-profile.interface';
|
||||||
import { OrderWithAccount } from '@ghostfolio/common/types';
|
import { OrderWithAccount } from '@ghostfolio/common/types';
|
||||||
import { AssetClass, AssetSubClass } from '@prisma/client';
|
|
||||||
|
|
||||||
export interface PortfolioPositionDetail {
|
export interface PortfolioPositionDetail {
|
||||||
assetClass?: AssetClass;
|
|
||||||
assetSubClass?: AssetSubClass;
|
|
||||||
averagePrice: number;
|
averagePrice: number;
|
||||||
currency: string;
|
|
||||||
firstBuyDate: string;
|
firstBuyDate: string;
|
||||||
grossPerformance: number;
|
grossPerformance: number;
|
||||||
grossPerformancePercent: number;
|
grossPerformancePercent: number;
|
||||||
@ -14,12 +11,11 @@ export interface PortfolioPositionDetail {
|
|||||||
marketPrice: number;
|
marketPrice: number;
|
||||||
maxPrice: number;
|
maxPrice: number;
|
||||||
minPrice: number;
|
minPrice: number;
|
||||||
name: string;
|
|
||||||
netPerformance: number;
|
netPerformance: number;
|
||||||
netPerformancePercent: number;
|
netPerformancePercent: number;
|
||||||
orders: OrderWithAccount[];
|
orders: OrderWithAccount[];
|
||||||
quantity: number;
|
quantity: number;
|
||||||
symbol: string;
|
SymbolProfile: EnhancedSymbolProfile;
|
||||||
transactionCount: number;
|
transactionCount: number;
|
||||||
value: number;
|
value: number;
|
||||||
}
|
}
|
||||||
|
@ -344,6 +344,7 @@ export class PortfolioController {
|
|||||||
|
|
||||||
@Get('position/:dataSource/:symbol')
|
@Get('position/:dataSource/:symbol')
|
||||||
@UseInterceptors(TransformDataSourceInRequestInterceptor)
|
@UseInterceptors(TransformDataSourceInRequestInterceptor)
|
||||||
|
@UseInterceptors(TransformDataSourceInResponseInterceptor)
|
||||||
@UseGuards(AuthGuard('jwt'))
|
@UseGuards(AuthGuard('jwt'))
|
||||||
public async getPosition(
|
public async getPosition(
|
||||||
@Headers('impersonation-id') impersonationId: string,
|
@Headers('impersonation-id') impersonationId: string,
|
||||||
|
@ -417,7 +417,6 @@ export class PortfolioServiceNew {
|
|||||||
if (orders.length <= 0) {
|
if (orders.length <= 0) {
|
||||||
return {
|
return {
|
||||||
averagePrice: undefined,
|
averagePrice: undefined,
|
||||||
currency: undefined,
|
|
||||||
firstBuyDate: undefined,
|
firstBuyDate: undefined,
|
||||||
grossPerformance: undefined,
|
grossPerformance: undefined,
|
||||||
grossPerformancePercent: undefined,
|
grossPerformancePercent: undefined,
|
||||||
@ -426,21 +425,20 @@ export class PortfolioServiceNew {
|
|||||||
marketPrice: undefined,
|
marketPrice: undefined,
|
||||||
maxPrice: undefined,
|
maxPrice: undefined,
|
||||||
minPrice: undefined,
|
minPrice: undefined,
|
||||||
name: undefined,
|
|
||||||
netPerformance: undefined,
|
netPerformance: undefined,
|
||||||
netPerformancePercent: undefined,
|
netPerformancePercent: undefined,
|
||||||
orders: [],
|
orders: [],
|
||||||
quantity: undefined,
|
quantity: undefined,
|
||||||
symbol: aSymbol,
|
SymbolProfile: undefined,
|
||||||
transactionCount: undefined,
|
transactionCount: undefined,
|
||||||
value: undefined
|
value: undefined
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const assetClass = orders[0].SymbolProfile?.assetClass;
|
|
||||||
const assetSubClass = orders[0].SymbolProfile?.assetSubClass;
|
|
||||||
const positionCurrency = orders[0].currency;
|
const positionCurrency = orders[0].currency;
|
||||||
const name = orders[0].SymbolProfile?.name ?? '';
|
const [SymbolProfile] = await this.symbolProfileService.getSymbolProfiles([
|
||||||
|
aSymbol
|
||||||
|
]);
|
||||||
|
|
||||||
const portfolioOrders: PortfolioOrder[] = orders
|
const portfolioOrders: PortfolioOrder[] = orders
|
||||||
.filter((order) => {
|
.filter((order) => {
|
||||||
@ -557,18 +555,15 @@ export class PortfolioServiceNew {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
assetClass,
|
|
||||||
assetSubClass,
|
|
||||||
currency,
|
|
||||||
firstBuyDate,
|
firstBuyDate,
|
||||||
grossPerformance,
|
grossPerformance,
|
||||||
investment,
|
investment,
|
||||||
marketPrice,
|
marketPrice,
|
||||||
maxPrice,
|
maxPrice,
|
||||||
minPrice,
|
minPrice,
|
||||||
name,
|
|
||||||
netPerformance,
|
netPerformance,
|
||||||
orders,
|
orders,
|
||||||
|
SymbolProfile,
|
||||||
transactionCount,
|
transactionCount,
|
||||||
averagePrice: averagePrice.toNumber(),
|
averagePrice: averagePrice.toNumber(),
|
||||||
grossPerformancePercent:
|
grossPerformancePercent:
|
||||||
@ -576,7 +571,6 @@ export class PortfolioServiceNew {
|
|||||||
historicalData: historicalDataArray,
|
historicalData: historicalDataArray,
|
||||||
netPerformancePercent: position.netPerformancePercentage?.toNumber(),
|
netPerformancePercent: position.netPerformancePercentage?.toNumber(),
|
||||||
quantity: quantity.toNumber(),
|
quantity: quantity.toNumber(),
|
||||||
symbol: aSymbol,
|
|
||||||
value: this.exchangeRateDataService.toCurrency(
|
value: this.exchangeRateDataService.toCurrency(
|
||||||
quantity.mul(marketPrice).toNumber(),
|
quantity.mul(marketPrice).toNumber(),
|
||||||
currency,
|
currency,
|
||||||
@ -621,15 +615,12 @@ export class PortfolioServiceNew {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
assetClass,
|
|
||||||
assetSubClass,
|
|
||||||
marketPrice,
|
marketPrice,
|
||||||
maxPrice,
|
maxPrice,
|
||||||
minPrice,
|
minPrice,
|
||||||
name,
|
|
||||||
orders,
|
orders,
|
||||||
|
SymbolProfile,
|
||||||
averagePrice: 0,
|
averagePrice: 0,
|
||||||
currency: currentData[aSymbol]?.currency,
|
|
||||||
firstBuyDate: undefined,
|
firstBuyDate: undefined,
|
||||||
grossPerformance: undefined,
|
grossPerformance: undefined,
|
||||||
grossPerformancePercent: undefined,
|
grossPerformancePercent: undefined,
|
||||||
@ -638,7 +629,6 @@ export class PortfolioServiceNew {
|
|||||||
netPerformance: undefined,
|
netPerformance: undefined,
|
||||||
netPerformancePercent: undefined,
|
netPerformancePercent: undefined,
|
||||||
quantity: 0,
|
quantity: 0,
|
||||||
symbol: aSymbol,
|
|
||||||
transactionCount: undefined,
|
transactionCount: undefined,
|
||||||
value: 0
|
value: 0
|
||||||
};
|
};
|
||||||
|
@ -405,7 +405,6 @@ export class PortfolioService {
|
|||||||
if (orders.length <= 0) {
|
if (orders.length <= 0) {
|
||||||
return {
|
return {
|
||||||
averagePrice: undefined,
|
averagePrice: undefined,
|
||||||
currency: undefined,
|
|
||||||
firstBuyDate: undefined,
|
firstBuyDate: undefined,
|
||||||
grossPerformance: undefined,
|
grossPerformance: undefined,
|
||||||
grossPerformancePercent: undefined,
|
grossPerformancePercent: undefined,
|
||||||
@ -414,21 +413,20 @@ export class PortfolioService {
|
|||||||
marketPrice: undefined,
|
marketPrice: undefined,
|
||||||
maxPrice: undefined,
|
maxPrice: undefined,
|
||||||
minPrice: undefined,
|
minPrice: undefined,
|
||||||
name: undefined,
|
|
||||||
netPerformance: undefined,
|
netPerformance: undefined,
|
||||||
netPerformancePercent: undefined,
|
netPerformancePercent: undefined,
|
||||||
orders: [],
|
orders: [],
|
||||||
quantity: undefined,
|
quantity: undefined,
|
||||||
symbol: aSymbol,
|
SymbolProfile: undefined,
|
||||||
transactionCount: undefined,
|
transactionCount: undefined,
|
||||||
value: undefined
|
value: undefined
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const assetClass = orders[0].SymbolProfile?.assetClass;
|
|
||||||
const assetSubClass = orders[0].SymbolProfile?.assetSubClass;
|
|
||||||
const positionCurrency = orders[0].currency;
|
const positionCurrency = orders[0].currency;
|
||||||
const name = orders[0].SymbolProfile?.name ?? '';
|
const [SymbolProfile] = await this.symbolProfileService.getSymbolProfiles([
|
||||||
|
aSymbol
|
||||||
|
]);
|
||||||
|
|
||||||
const portfolioOrders: PortfolioOrder[] = orders
|
const portfolioOrders: PortfolioOrder[] = orders
|
||||||
.filter((order) => {
|
.filter((order) => {
|
||||||
@ -543,25 +541,21 @@ export class PortfolioService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
assetClass,
|
|
||||||
assetSubClass,
|
|
||||||
currency,
|
|
||||||
firstBuyDate,
|
firstBuyDate,
|
||||||
grossPerformance,
|
grossPerformance,
|
||||||
investment,
|
investment,
|
||||||
marketPrice,
|
marketPrice,
|
||||||
maxPrice,
|
maxPrice,
|
||||||
minPrice,
|
minPrice,
|
||||||
name,
|
|
||||||
netPerformance,
|
netPerformance,
|
||||||
orders,
|
orders,
|
||||||
|
SymbolProfile,
|
||||||
transactionCount,
|
transactionCount,
|
||||||
averagePrice: averagePrice.toNumber(),
|
averagePrice: averagePrice.toNumber(),
|
||||||
grossPerformancePercent: position.grossPerformancePercentage.toNumber(),
|
grossPerformancePercent: position.grossPerformancePercentage.toNumber(),
|
||||||
historicalData: historicalDataArray,
|
historicalData: historicalDataArray,
|
||||||
netPerformancePercent: position.netPerformancePercentage.toNumber(),
|
netPerformancePercent: position.netPerformancePercentage.toNumber(),
|
||||||
quantity: quantity.toNumber(),
|
quantity: quantity.toNumber(),
|
||||||
symbol: aSymbol,
|
|
||||||
value: this.exchangeRateDataService.toCurrency(
|
value: this.exchangeRateDataService.toCurrency(
|
||||||
quantity.mul(marketPrice).toNumber(),
|
quantity.mul(marketPrice).toNumber(),
|
||||||
currency,
|
currency,
|
||||||
@ -606,15 +600,12 @@ export class PortfolioService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
assetClass,
|
|
||||||
assetSubClass,
|
|
||||||
marketPrice,
|
marketPrice,
|
||||||
maxPrice,
|
maxPrice,
|
||||||
minPrice,
|
minPrice,
|
||||||
name,
|
|
||||||
orders,
|
orders,
|
||||||
|
SymbolProfile,
|
||||||
averagePrice: 0,
|
averagePrice: 0,
|
||||||
currency: currentData[aSymbol]?.currency,
|
|
||||||
firstBuyDate: undefined,
|
firstBuyDate: undefined,
|
||||||
grossPerformance: undefined,
|
grossPerformance: undefined,
|
||||||
grossPerformancePercent: undefined,
|
grossPerformancePercent: undefined,
|
||||||
@ -623,7 +614,6 @@ export class PortfolioService {
|
|||||||
netPerformance: undefined,
|
netPerformance: undefined,
|
||||||
netPerformancePercent: undefined,
|
netPerformancePercent: undefined,
|
||||||
quantity: 0,
|
quantity: 0,
|
||||||
symbol: aSymbol,
|
|
||||||
transactionCount: undefined,
|
transactionCount: undefined,
|
||||||
value: 0
|
value: 0
|
||||||
};
|
};
|
||||||
|
@ -58,12 +58,25 @@ export class TransformDataSourceInResponseInterceptor<T>
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (data.orders) {
|
||||||
|
data.orders.map((order) => {
|
||||||
|
order.dataSource = encodeDataSource(order.dataSource);
|
||||||
|
return order;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (data.positions) {
|
if (data.positions) {
|
||||||
data.positions.map((position) => {
|
data.positions.map((position) => {
|
||||||
position.dataSource = encodeDataSource(position.dataSource);
|
position.dataSource = encodeDataSource(position.dataSource);
|
||||||
return position;
|
return position;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (data.SymbolProfile) {
|
||||||
|
data.SymbolProfile.dataSource = encodeDataSource(
|
||||||
|
data.SymbolProfile.dataSource
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
|
@ -11,7 +11,7 @@ import { DataService } from '@ghostfolio/client/services/data.service';
|
|||||||
import { DATE_FORMAT, downloadAsFile } from '@ghostfolio/common/helper';
|
import { DATE_FORMAT, downloadAsFile } from '@ghostfolio/common/helper';
|
||||||
import { OrderWithAccount } from '@ghostfolio/common/types';
|
import { OrderWithAccount } from '@ghostfolio/common/types';
|
||||||
import { LineChartItem } from '@ghostfolio/ui/line-chart/interfaces/line-chart.interface';
|
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 { format, isSameMonth, isToday, parseISO } from 'date-fns';
|
||||||
import { Subject } from 'rxjs';
|
import { Subject } from 'rxjs';
|
||||||
import { takeUntil } from 'rxjs/operators';
|
import { takeUntil } from 'rxjs/operators';
|
||||||
@ -26,10 +26,11 @@ import { PositionDetailDialogParams } from './interfaces/interfaces';
|
|||||||
styleUrls: ['./position-detail-dialog.component.scss']
|
styleUrls: ['./position-detail-dialog.component.scss']
|
||||||
})
|
})
|
||||||
export class PositionDetailDialog implements OnDestroy, OnInit {
|
export class PositionDetailDialog implements OnDestroy, OnInit {
|
||||||
public assetSubClass: AssetSubClass;
|
|
||||||
public averagePrice: number;
|
public averagePrice: number;
|
||||||
public benchmarkDataItems: LineChartItem[];
|
public benchmarkDataItems: LineChartItem[];
|
||||||
public currency: string;
|
public countries: {
|
||||||
|
[code: string]: { name: string; value: number };
|
||||||
|
};
|
||||||
public firstBuyDate: string;
|
public firstBuyDate: string;
|
||||||
public grossPerformance: number;
|
public grossPerformance: number;
|
||||||
public grossPerformancePercent: number;
|
public grossPerformancePercent: number;
|
||||||
@ -38,13 +39,15 @@ export class PositionDetailDialog implements OnDestroy, OnInit {
|
|||||||
public marketPrice: number;
|
public marketPrice: number;
|
||||||
public maxPrice: number;
|
public maxPrice: number;
|
||||||
public minPrice: number;
|
public minPrice: number;
|
||||||
public name: string;
|
|
||||||
public netPerformance: number;
|
public netPerformance: number;
|
||||||
public netPerformancePercent: number;
|
public netPerformancePercent: number;
|
||||||
public orders: OrderWithAccount[];
|
public orders: OrderWithAccount[];
|
||||||
public quantity: number;
|
public quantity: number;
|
||||||
public quantityPrecision = 2;
|
public quantityPrecision = 2;
|
||||||
public symbol: string;
|
public sectors: {
|
||||||
|
[name: string]: { name: string; value: number };
|
||||||
|
};
|
||||||
|
public SymbolProfile: SymbolProfile;
|
||||||
public transactionCount: number;
|
public transactionCount: number;
|
||||||
public value: number;
|
public value: number;
|
||||||
|
|
||||||
@ -66,9 +69,7 @@ export class PositionDetailDialog implements OnDestroy, OnInit {
|
|||||||
.pipe(takeUntil(this.unsubscribeSubject))
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
.subscribe(
|
.subscribe(
|
||||||
({
|
({
|
||||||
assetSubClass,
|
|
||||||
averagePrice,
|
averagePrice,
|
||||||
currency,
|
|
||||||
firstBuyDate,
|
firstBuyDate,
|
||||||
grossPerformance,
|
grossPerformance,
|
||||||
grossPerformancePercent,
|
grossPerformancePercent,
|
||||||
@ -77,19 +78,17 @@ export class PositionDetailDialog implements OnDestroy, OnInit {
|
|||||||
marketPrice,
|
marketPrice,
|
||||||
maxPrice,
|
maxPrice,
|
||||||
minPrice,
|
minPrice,
|
||||||
name,
|
|
||||||
netPerformance,
|
netPerformance,
|
||||||
netPerformancePercent,
|
netPerformancePercent,
|
||||||
orders,
|
orders,
|
||||||
quantity,
|
quantity,
|
||||||
symbol,
|
SymbolProfile,
|
||||||
transactionCount,
|
transactionCount,
|
||||||
value
|
value
|
||||||
}) => {
|
}) => {
|
||||||
this.assetSubClass = assetSubClass;
|
|
||||||
this.averagePrice = averagePrice;
|
this.averagePrice = averagePrice;
|
||||||
this.benchmarkDataItems = [];
|
this.benchmarkDataItems = [];
|
||||||
this.currency = currency;
|
this.countries = {};
|
||||||
this.firstBuyDate = firstBuyDate;
|
this.firstBuyDate = firstBuyDate;
|
||||||
this.grossPerformance = grossPerformance;
|
this.grossPerformance = grossPerformance;
|
||||||
this.grossPerformancePercent = grossPerformancePercent;
|
this.grossPerformancePercent = grossPerformancePercent;
|
||||||
@ -110,15 +109,33 @@ export class PositionDetailDialog implements OnDestroy, OnInit {
|
|||||||
this.marketPrice = marketPrice;
|
this.marketPrice = marketPrice;
|
||||||
this.maxPrice = maxPrice;
|
this.maxPrice = maxPrice;
|
||||||
this.minPrice = minPrice;
|
this.minPrice = minPrice;
|
||||||
this.name = name;
|
|
||||||
this.netPerformance = netPerformance;
|
this.netPerformance = netPerformance;
|
||||||
this.netPerformancePercent = netPerformancePercent;
|
this.netPerformancePercent = netPerformancePercent;
|
||||||
this.orders = orders;
|
this.orders = orders;
|
||||||
this.quantity = quantity;
|
this.quantity = quantity;
|
||||||
this.symbol = symbol;
|
this.sectors = {};
|
||||||
|
this.SymbolProfile = SymbolProfile;
|
||||||
this.transactionCount = transactionCount;
|
this.transactionCount = transactionCount;
|
||||||
this.value = value;
|
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))) {
|
if (isToday(parseISO(this.firstBuyDate))) {
|
||||||
// Add average price
|
// Add average price
|
||||||
this.historicalDataItems.push({
|
this.historicalDataItems.push({
|
||||||
@ -166,7 +183,7 @@ export class PositionDetailDialog implements OnDestroy, OnInit {
|
|||||||
|
|
||||||
if (Number.isInteger(this.quantity)) {
|
if (Number.isInteger(this.quantity)) {
|
||||||
this.quantityPrecision = 0;
|
this.quantityPrecision = 0;
|
||||||
} else if (assetSubClass === 'CRYPTOCURRENCY') {
|
} else if (this.SymbolProfile?.assetSubClass === 'CRYPTOCURRENCY') {
|
||||||
if (this.quantity < 1) {
|
if (this.quantity < 1) {
|
||||||
this.quantityPrecision = 7;
|
this.quantityPrecision = 7;
|
||||||
} else if (this.quantity < 1000) {
|
} else if (this.quantity < 1000) {
|
||||||
@ -196,7 +213,7 @@ export class PositionDetailDialog implements OnDestroy, OnInit {
|
|||||||
.subscribe((data) => {
|
.subscribe((data) => {
|
||||||
downloadAsFile(
|
downloadAsFile(
|
||||||
data,
|
data,
|
||||||
`ghostfolio-export-${this.symbol}-${format(
|
`ghostfolio-export-${this.SymbolProfile?.symbol}-${format(
|
||||||
parseISO(data.meta.date),
|
parseISO(data.meta.date),
|
||||||
'yyyyMMddHHmm'
|
'yyyyMMddHHmm'
|
||||||
)}.json`,
|
)}.json`,
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
mat-dialog-title
|
mat-dialog-title
|
||||||
position="center"
|
position="center"
|
||||||
[deviceType]="data.deviceType"
|
[deviceType]="data.deviceType"
|
||||||
[title]="name ?? symbol"
|
[title]="SymbolProfile?.name ?? SymbolProfile?.symbol"
|
||||||
(closeButtonClicked)="onClose()"
|
(closeButtonClicked)="onClose()"
|
||||||
></gf-dialog-header>
|
></gf-dialog-header>
|
||||||
|
|
||||||
@ -55,7 +55,7 @@
|
|||||||
<gf-value
|
<gf-value
|
||||||
label="Ø Buy Price"
|
label="Ø Buy Price"
|
||||||
size="medium"
|
size="medium"
|
||||||
[currency]="currency"
|
[currency]="SymbolProfile?.currency"
|
||||||
[locale]="data.locale"
|
[locale]="data.locale"
|
||||||
[value]="averagePrice"
|
[value]="averagePrice"
|
||||||
></gf-value>
|
></gf-value>
|
||||||
@ -64,7 +64,7 @@
|
|||||||
<gf-value
|
<gf-value
|
||||||
label="Market Price"
|
label="Market Price"
|
||||||
size="medium"
|
size="medium"
|
||||||
[currency]="currency"
|
[currency]="SymbolProfile?.currency"
|
||||||
[locale]="data.locale"
|
[locale]="data.locale"
|
||||||
[value]="marketPrice"
|
[value]="marketPrice"
|
||||||
></gf-value>
|
></gf-value>
|
||||||
@ -73,7 +73,7 @@
|
|||||||
<gf-value
|
<gf-value
|
||||||
label="Minimum Price"
|
label="Minimum Price"
|
||||||
size="medium"
|
size="medium"
|
||||||
[currency]="currency"
|
[currency]="SymbolProfile?.currency"
|
||||||
[locale]="data.locale"
|
[locale]="data.locale"
|
||||||
[ngClass]="{ 'text-danger': minPrice?.toFixed(2) === marketPrice?.toFixed(2) && maxPrice?.toFixed(2) !== minPrice?.toFixed(2) }"
|
[ngClass]="{ 'text-danger': minPrice?.toFixed(2) === marketPrice?.toFixed(2) && maxPrice?.toFixed(2) !== minPrice?.toFixed(2) }"
|
||||||
[value]="minPrice"
|
[value]="minPrice"
|
||||||
@ -83,7 +83,7 @@
|
|||||||
<gf-value
|
<gf-value
|
||||||
label="Maximum Price"
|
label="Maximum Price"
|
||||||
size="medium"
|
size="medium"
|
||||||
[currency]="currency"
|
[currency]="SymbolProfile?.currency"
|
||||||
[locale]="data.locale"
|
[locale]="data.locale"
|
||||||
[ngClass]="{ 'text-success': maxPrice?.toFixed(2) === marketPrice?.toFixed(2) && maxPrice?.toFixed(2) !== minPrice?.toFixed(2) }"
|
[ngClass]="{ 'text-success': maxPrice?.toFixed(2) === marketPrice?.toFixed(2) && maxPrice?.toFixed(2) !== minPrice?.toFixed(2) }"
|
||||||
[value]="maxPrice"
|
[value]="maxPrice"
|
||||||
@ -122,6 +122,72 @@
|
|||||||
[value]="transactionCount"
|
[value]="transactionCount"
|
||||||
></gf-value>
|
></gf-value>
|
||||||
</div>
|
</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>
|
||||||
</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 { GfDialogHeaderModule } from '@ghostfolio/client/components/dialog-header/dialog-header.module';
|
||||||
import { GfActivitiesTableModule } from '@ghostfolio/ui/activities-table/activities-table.module';
|
import { GfActivitiesTableModule } from '@ghostfolio/ui/activities-table/activities-table.module';
|
||||||
import { GfLineChartModule } from '@ghostfolio/ui/line-chart/line-chart.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 { GfValueModule } from '@ghostfolio/ui/value';
|
||||||
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
|
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
|
||||||
|
|
||||||
@ -20,6 +21,7 @@ import { PositionDetailDialog } from './position-detail-dialog.component';
|
|||||||
GfDialogFooterModule,
|
GfDialogFooterModule,
|
||||||
GfDialogHeaderModule,
|
GfDialogHeaderModule,
|
||||||
GfLineChartModule,
|
GfLineChartModule,
|
||||||
|
GfPortfolioProportionChartModule,
|
||||||
GfValueModule,
|
GfValueModule,
|
||||||
MatButtonModule,
|
MatButtonModule,
|
||||||
MatDialogModule,
|
MatDialogModule,
|
||||||
|
@ -34,12 +34,12 @@
|
|||||||
{{ currency }}
|
{{ currency }}
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-container *ngIf="isDate">
|
<ng-container *ngIf="isString">
|
||||||
<div
|
<div
|
||||||
class="mb-0"
|
class="mb-0 text-truncate"
|
||||||
[ngClass]="{ h2: size === 'large', h4: size === 'medium' }"
|
[ngClass]="{ h2: size === 'large', h4: size === 'medium' }"
|
||||||
>
|
>
|
||||||
{{ formattedDate }}
|
{{ formattedValue | titlecase }}
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
|
@ -5,7 +5,7 @@ import {
|
|||||||
OnChanges
|
OnChanges
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { DEFAULT_DATE_FORMAT } from '@ghostfolio/common/config';
|
import { DEFAULT_DATE_FORMAT } from '@ghostfolio/common/config';
|
||||||
import { format, isDate } from 'date-fns';
|
import { format, isDate, parseISO } from 'date-fns';
|
||||||
import { isNumber } from 'lodash';
|
import { isNumber } from 'lodash';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -28,10 +28,9 @@ export class ValueComponent implements OnChanges {
|
|||||||
@Input() value: number | string = '';
|
@Input() value: number | string = '';
|
||||||
|
|
||||||
public absoluteValue = 0;
|
public absoluteValue = 0;
|
||||||
public formattedDate = '';
|
|
||||||
public formattedValue = '';
|
public formattedValue = '';
|
||||||
public isDate = false;
|
|
||||||
public isNumber = false;
|
public isNumber = false;
|
||||||
|
public isString = false;
|
||||||
public useAbsoluteValue = false;
|
public useAbsoluteValue = false;
|
||||||
|
|
||||||
public constructor() {}
|
public constructor() {}
|
||||||
@ -39,8 +38,8 @@ export class ValueComponent implements OnChanges {
|
|||||||
public ngOnChanges() {
|
public ngOnChanges() {
|
||||||
if (this.value || this.value === 0) {
|
if (this.value || this.value === 0) {
|
||||||
if (isNumber(this.value)) {
|
if (isNumber(this.value)) {
|
||||||
this.isDate = false;
|
|
||||||
this.isNumber = true;
|
this.isNumber = true;
|
||||||
|
this.isString = false;
|
||||||
this.absoluteValue = Math.abs(<number>this.value);
|
this.absoluteValue = Math.abs(<number>this.value);
|
||||||
|
|
||||||
if (this.colorizeSign) {
|
if (this.colorizeSign) {
|
||||||
@ -98,17 +97,19 @@ export class ValueComponent implements OnChanges {
|
|||||||
this.formattedValue = this.formattedValue.replace(/^-/, '');
|
this.formattedValue = this.formattedValue.replace(/^-/, '');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
try {
|
this.isNumber = false;
|
||||||
if (isDate(new Date(this.value))) {
|
this.isString = true;
|
||||||
this.isDate = true;
|
|
||||||
this.isNumber = false;
|
|
||||||
|
|
||||||
this.formattedDate = format(
|
try {
|
||||||
|
if (isDate(parseISO(this.value))) {
|
||||||
|
this.formattedValue = format(
|
||||||
new Date(<string>this.value),
|
new Date(<string>this.value),
|
||||||
DEFAULT_DATE_FORMAT
|
DEFAULT_DATE_FORMAT
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch {}
|
} catch {
|
||||||
|
this.formattedValue = this.value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user