Browse Source

Add countries and sectors to position detail dialog

pull/692/head
Thomas 3 years ago
parent
commit
215f11db0a
  1. 2
      apps/api/src/app/portfolio/interfaces/portfolio-position-detail.interface.ts
  2. 8
      apps/api/src/app/portfolio/portfolio.service-new.ts
  3. 8
      apps/api/src/app/portfolio/portfolio.service.ts
  4. 31
      apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.component.ts
  5. 51
      apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.html
  6. 2
      apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.module.ts
  7. 6
      libs/ui/src/lib/value/value.component.html
  8. 21
      libs/ui/src/lib/value/value.component.ts

2
apps/api/src/app/portfolio/interfaces/portfolio-position-detail.interface.ts

@ -1,3 +1,4 @@
import { EnhancedSymbolProfile } from '@ghostfolio/api/services/interfaces/symbol-profile.interface';
import { OrderWithAccount } from '@ghostfolio/common/types';
import { AssetClass, AssetSubClass } from '@prisma/client';
@ -19,6 +20,7 @@ export interface PortfolioPositionDetail {
netPerformancePercent: number;
orders: OrderWithAccount[];
quantity: number;
SymbolProfile: EnhancedSymbolProfile;
symbol: string;
transactionCount: number;
value: number;

8
apps/api/src/app/portfolio/portfolio.service-new.ts

@ -432,6 +432,7 @@ export class PortfolioServiceNew {
orders: [],
quantity: undefined,
symbol: aSymbol,
SymbolProfile: undefined,
transactionCount: undefined,
value: undefined
};
@ -439,8 +440,11 @@ export class PortfolioServiceNew {
const assetClass = orders[0].SymbolProfile?.assetClass;
const assetSubClass = orders[0].SymbolProfile?.assetSubClass;
const positionCurrency = orders[0].currency;
const name = orders[0].SymbolProfile?.name ?? '';
const positionCurrency = orders[0].currency;
const [SymbolProfile] = await this.symbolProfileService.getSymbolProfiles([
aSymbol
]);
const portfolioOrders: PortfolioOrder[] = orders
.filter((order) => {
@ -569,6 +573,7 @@ export class PortfolioServiceNew {
name,
netPerformance,
orders,
SymbolProfile,
transactionCount,
averagePrice: averagePrice.toNumber(),
grossPerformancePercent:
@ -628,6 +633,7 @@ export class PortfolioServiceNew {
minPrice,
name,
orders,
SymbolProfile,
averagePrice: 0,
currency: currentData[aSymbol]?.currency,
firstBuyDate: undefined,

8
apps/api/src/app/portfolio/portfolio.service.ts

@ -420,6 +420,7 @@ export class PortfolioService {
orders: [],
quantity: undefined,
symbol: aSymbol,
SymbolProfile: undefined,
transactionCount: undefined,
value: undefined
};
@ -427,8 +428,11 @@ export class PortfolioService {
const assetClass = orders[0].SymbolProfile?.assetClass;
const assetSubClass = orders[0].SymbolProfile?.assetSubClass;
const positionCurrency = orders[0].currency;
const name = orders[0].SymbolProfile?.name ?? '';
const positionCurrency = orders[0].currency;
const [SymbolProfile] = await this.symbolProfileService.getSymbolProfiles([
aSymbol
]);
const portfolioOrders: PortfolioOrder[] = orders
.filter((order) => {
@ -555,6 +559,7 @@ export class PortfolioService {
name,
netPerformance,
orders,
SymbolProfile,
transactionCount,
averagePrice: averagePrice.toNumber(),
grossPerformancePercent: position.grossPerformancePercentage.toNumber(),
@ -613,6 +618,7 @@ export class PortfolioService {
minPrice,
name,
orders,
SymbolProfile,
averagePrice: 0,
currency: currentData[aSymbol]?.currency,
firstBuyDate: undefined,

31
apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.component.ts

@ -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 { AssetSubClass, SymbolProfile } from '@prisma/client';
import { format, isSameMonth, isToday, parseISO } from 'date-fns';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
@ -29,6 +29,9 @@ export class PositionDetailDialog implements OnDestroy, OnInit {
public assetSubClass: AssetSubClass;
public averagePrice: number;
public benchmarkDataItems: LineChartItem[];
public countries: {
[code: string]: { name: string; value: number };
};
public currency: string;
public firstBuyDate: string;
public grossPerformance: number;
@ -44,7 +47,11 @@ export class PositionDetailDialog implements OnDestroy, OnInit {
public orders: OrderWithAccount[];
public quantity: number;
public quantityPrecision = 2;
public sectors: {
[name: string]: { name: string; value: number };
};
public symbol: string;
public SymbolProfile: SymbolProfile;
public transactionCount: number;
public value: number;
@ -83,12 +90,14 @@ export class PositionDetailDialog implements OnDestroy, OnInit {
orders,
quantity,
symbol,
SymbolProfile,
transactionCount,
value
}) => {
this.assetSubClass = assetSubClass;
this.averagePrice = averagePrice;
this.benchmarkDataItems = [];
this.countries = {};
this.currency = currency;
this.firstBuyDate = firstBuyDate;
this.grossPerformance = grossPerformance;
@ -115,10 +124,30 @@ export class PositionDetailDialog implements OnDestroy, OnInit {
this.netPerformancePercent = netPerformancePercent;
this.orders = orders;
this.quantity = quantity;
this.sectors = {};
this.symbol = symbol;
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({

51
apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.html

@ -122,6 +122,57 @@
[value]="transactionCount"
></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>

2
apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.module.ts

@ -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,

6
libs/ui/src/lib/value/value.component.html

@ -34,12 +34,12 @@
{{ currency }}
</div>
</ng-container>
<ng-container *ngIf="isDate">
<ng-container *ngIf="isString">
<div
class="mb-0"
class="mb-0 text-truncate"
[ngClass]="{ h2: size === 'large', h4: size === 'medium' }"
>
{{ formattedDate }}
{{ formattedValue }}
</div>
</ng-container>
</div>

21
libs/ui/src/lib/value/value.component.ts

@ -5,7 +5,7 @@ import {
OnChanges
} from '@angular/core';
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';
@Component({
@ -28,10 +28,9 @@ export class ValueComponent implements OnChanges {
@Input() value: number | string = '';
public absoluteValue = 0;
public formattedDate = '';
public formattedValue = '';
public isDate = false;
public isNumber = false;
public isString = false;
public useAbsoluteValue = false;
public constructor() {}
@ -39,8 +38,8 @@ export class ValueComponent implements OnChanges {
public ngOnChanges() {
if (this.value || this.value === 0) {
if (isNumber(this.value)) {
this.isDate = false;
this.isNumber = true;
this.isString = false;
this.absoluteValue = Math.abs(<number>this.value);
if (this.colorizeSign) {
@ -98,17 +97,19 @@ export class ValueComponent implements OnChanges {
this.formattedValue = this.formattedValue.replace(/^-/, '');
}
} else {
try {
if (isDate(new Date(this.value))) {
this.isDate = true;
this.isNumber = false;
this.isNumber = false;
this.isString = true;
this.formattedDate = format(
try {
if (isDate(parseISO(this.value))) {
this.formattedValue = format(
new Date(<string>this.value),
DEFAULT_DATE_FORMAT
);
}
} catch {}
} catch {
this.formattedValue = this.value;
}
}
}

Loading…
Cancel
Save