Browse Source

Refactoring and integrate in public page

pull/767/head
Thomas 3 years ago
parent
commit
ce04656c92
  1. 3
      apps/api/src/app/portfolio/portfolio-service.strategy.ts
  2. 5
      apps/api/src/app/portfolio/portfolio.controller.ts
  3. 15
      apps/api/src/app/portfolio/portfolio.service-new.ts
  4. 48
      apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts
  5. 19
      apps/client/src/app/pages/portfolio/allocations/allocations-page.html
  6. 37
      apps/client/src/app/pages/public/public-page.component.ts
  7. 28
      apps/client/src/app/pages/public/public-page.html
  8. 2
      apps/client/src/app/pages/public/public-page.module.ts
  9. 4
      apps/client/src/styles.scss
  10. 3
      libs/common/src/lib/interfaces/portfolio-position.interface.ts
  11. 1
      libs/common/src/lib/interfaces/portfolio-public-details.interface.ts
  12. 2
      libs/common/src/lib/types/index.ts
  13. 1
      libs/common/src/lib/types/market.type.ts
  14. 2
      libs/ui/src/lib/value/value.component.ts

3
apps/api/src/app/portfolio/portfolio-service.strategy.ts

@ -13,8 +13,9 @@ export class PortfolioServiceStrategy {
@Inject(REQUEST) private readonly request: RequestWithUser @Inject(REQUEST) private readonly request: RequestWithUser
) {} ) {}
public get() { public get(newCalculationEngine?: boolean) {
if ( if (
newCalculationEngine ||
this.request.user?.Settings?.settings?.['isNewCalculationEngine'] === true this.request.user?.Settings?.settings?.['isNewCalculationEngine'] === true
) { ) {
return this.portfolioServiceNew; return this.portfolioServiceNew;

5
apps/api/src/app/portfolio/portfolio.controller.ts

@ -120,7 +120,7 @@ export class PortfolioController {
const { accounts, holdings, hasErrors } = const { accounts, holdings, hasErrors } =
await this.portfolioServiceStrategy await this.portfolioServiceStrategy
.get() .get(true)
.getDetails(impersonationId, this.request.user.id, range); .getDetails(impersonationId, this.request.user.id, range);
if (hasErrors || hasNotDefinedValuesInObject(holdings)) { if (hasErrors || hasNotDefinedValuesInObject(holdings)) {
@ -277,7 +277,7 @@ export class PortfolioController {
} }
const { holdings } = await this.portfolioServiceStrategy const { holdings } = await this.portfolioServiceStrategy
.get() .get(true)
.getDetails(access.userId, access.userId); .getDetails(access.userId, access.userId);
const portfolioPublicDetails: PortfolioPublicDetails = { const portfolioPublicDetails: PortfolioPublicDetails = {
@ -304,6 +304,7 @@ export class PortfolioController {
allocationCurrent: portfolioPosition.allocationCurrent, allocationCurrent: portfolioPosition.allocationCurrent,
countries: hasDetails ? portfolioPosition.countries : [], countries: hasDetails ? portfolioPosition.countries : [],
currency: portfolioPosition.currency, currency: portfolioPosition.currency,
markets: portfolioPosition.markets,
name: portfolioPosition.name, name: portfolioPosition.name,
sectors: hasDetails ? portfolioPosition.sectors : [], sectors: hasDetails ? portfolioPosition.sectors : [],
value: portfolioPosition.value / totalValue value: portfolioPosition.value / totalValue

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

@ -40,6 +40,7 @@ import { InvestmentItem } from '@ghostfolio/common/interfaces/investment-item.in
import type { import type {
AccountWithValue, AccountWithValue,
DateRange, DateRange,
Market,
OrderWithAccount, OrderWithAccount,
RequestWithUser RequestWithUser
} from '@ghostfolio/common/types'; } from '@ghostfolio/common/types';
@ -384,23 +385,23 @@ export class PortfolioServiceNew {
const symbolProfile = symbolProfileMap[item.symbol]; const symbolProfile = symbolProfileMap[item.symbol];
const dataProviderResponse = dataProviderResponses[item.symbol]; const dataProviderResponse = dataProviderResponses[item.symbol];
const markets = { const markets: { [key in Market]: number } = {
DEVELOPED_MARKETS: 0, developedMarkets: 0,
EMERGING_MARKETS: 0, emergingMarkets: 0,
OTHER_MARKETS: 0 otherMarkets: 0
}; };
for (const country of symbolProfile.countries) { for (const country of symbolProfile.countries) {
if (developedMarkets.includes(country.code)) { if (developedMarkets.includes(country.code)) {
markets.DEVELOPED_MARKETS = new Big(markets.DEVELOPED_MARKETS) markets.developedMarkets = new Big(markets.developedMarkets)
.plus(country.weight) .plus(country.weight)
.toNumber(); .toNumber();
} else if (emergingMarkets.includes(country.code)) { } else if (emergingMarkets.includes(country.code)) {
markets.EMERGING_MARKETS = new Big(markets.EMERGING_MARKETS) markets.emergingMarkets = new Big(markets.emergingMarkets)
.plus(country.weight) .plus(country.weight)
.toNumber(); .toNumber();
} else { } else {
markets.OTHER_MARKETS = new Big(markets.OTHER_MARKETS) markets.otherMarkets = new Big(markets.otherMarkets)
.plus(country.weight) .plus(country.weight)
.toNumber(); .toNumber();
} }

48
apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts

@ -14,7 +14,7 @@ import {
User User
} from '@ghostfolio/common/interfaces'; } from '@ghostfolio/common/interfaces';
import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import { ToggleOption } from '@ghostfolio/common/types'; import { Market, ToggleOption } from '@ghostfolio/common/types';
import { Account, AssetClass, DataSource } from '@prisma/client'; import { Account, AssetClass, DataSource } from '@prisma/client';
import { DeviceDetectorService } from 'ngx-device-detector'; import { DeviceDetectorService } from 'ngx-device-detector';
import { Subject, Subscription } from 'rxjs'; import { Subject, Subscription } from 'rxjs';
@ -43,7 +43,7 @@ export class AllocationsPageComponent implements OnDestroy, OnInit {
public hasImpersonationId: boolean; public hasImpersonationId: boolean;
public hasPermissionToCreateOrder: boolean; public hasPermissionToCreateOrder: boolean;
public markets: { public markets: {
[market: string]: { name: string; value: number }; [key in Market]: { name: string; value: number };
}; };
public period = 'current'; public period = 'current';
public periodOptions: ToggleOption[] = [ public periodOptions: ToggleOption[] = [
@ -164,16 +164,16 @@ export class AllocationsPageComponent implements OnDestroy, OnInit {
} }
}; };
this.markets = { this.markets = {
DEVELOPED_MARKETS: { developedMarkets: {
name: 'DEVELOPED_MARKETS', name: 'developedMarkets',
value: 0 value: 0
}, },
EMERGING_MARKETS: { emergingMarkets: {
name: 'EMERGING_MARKETS', name: 'emergingMarkets',
value: 0 value: 0
}, },
OTHER_MARKETS: { otherMarkets: {
name: 'OTHER_MARKETS', name: 'otherMarkets',
value: 0 value: 0
} }
}; };
@ -236,14 +236,14 @@ export class AllocationsPageComponent implements OnDestroy, OnInit {
// Prepare analysis data by continents, countries and sectors except for cash // Prepare analysis data by continents, countries and sectors except for cash
if (position.countries.length > 0) { if (position.countries.length > 0) {
this.markets['DEVELOPED_MARKETS'].value += this.markets['developedMarkets'].value +=
position.markets['DEVELOPED_MARKETS'] * position.markets['developedMarkets'] *
(aPeriod === 'original' ? position.investment : position.value); (aPeriod === 'original' ? position.investment : position.value);
this.markets['EMERGING_MARKETS'].value += this.markets['emergingMarkets'].value +=
position.markets['EMERGING_MARKETS'] * position.markets['emergingMarkets'] *
(aPeriod === 'original' ? position.investment : position.value); (aPeriod === 'original' ? position.investment : position.value);
this.markets['OTHER_MARKETS'].value += this.markets['otherMarkets'].value +=
position.markets['OTHER_MARKETS'] * position.markets['otherMarkets'] *
(aPeriod === 'original' ? position.investment : position.value); (aPeriod === 'original' ? position.investment : position.value);
for (const country of position.countries) { for (const country of position.countries) {
@ -323,16 +323,16 @@ export class AllocationsPageComponent implements OnDestroy, OnInit {
} }
const marketsTotal = const marketsTotal =
this.markets['DEVELOPED_MARKETS'].value + this.markets.developedMarkets.value +
this.markets['EMERGING_MARKETS'].value + this.markets.emergingMarkets.value +
this.markets['OTHER_MARKETS'].value; this.markets.otherMarkets.value;
this.markets['DEVELOPED_MARKETS'].value = this.markets.developedMarkets.value =
this.markets['DEVELOPED_MARKETS'].value / marketsTotal; this.markets.developedMarkets.value / marketsTotal;
this.markets['EMERGING_MARKETS'].value = this.markets.emergingMarkets.value =
this.markets['EMERGING_MARKETS'].value / marketsTotal; this.markets.emergingMarkets.value / marketsTotal;
this.markets['OTHER_MARKETS'].value = this.markets.otherMarkets.value =
this.markets['OTHER_MARKETS'].value / marketsTotal; this.markets.otherMarkets.value / marketsTotal;
} }
public onChangePeriod(aValue: string) { public onChangePeriod(aValue: string) {

19
apps/client/src/app/pages/portfolio/allocations/allocations-page.html

@ -191,26 +191,31 @@
[isInPercent]="hasImpersonationId || user.settings.isRestrictedView" [isInPercent]="hasImpersonationId || user.settings.isRestrictedView"
></gf-world-map-chart> ></gf-world-map-chart>
<div class="row"> <div class="row">
<div class="col-xs-12 col-md-4 d-flex justify-content-center"> <div class="col-xs-12 col-md-4 my-2">
<gf-value <gf-value
label="Developed Markets" label="Developed Markets"
size="large" size="large"
[value]="(markets?.DEVELOPED_MARKETS?.value | percent: '1.2-2') || '-'" [isPercent]="true"
[locale]="user?.settings?.locale"
[value]="markets?.developedMarkets?.value"
></gf-value> ></gf-value>
</div> </div>
<div class="col-xs-12 col-md-4 d-flex justify-content-center"> <div class="col-xs-12 col-md-4 my-2">
<gf-value <gf-value
label="Emerging Markets" label="Emerging Markets"
size="large" size="large"
[value]="(markets?.EMERGING_MARKETS?.value | percent: '1.2-2') || '-'" [isPercent]="true"
[locale]="user?.settings?.locale"
[value]="markets?.emergingMarkets?.value"
></gf-value> ></gf-value>
</div> </div>
<div class="col-xs-12 col-md-4 d-flex justify-content-center"> <div class="col-xs-12 col-md-4 my-2">
<gf-value <gf-value
class="width-auto"
label="Other Markets" label="Other Markets"
size="large" size="large"
[value]="(markets?.OTHER_MARKETS?.value | percent: '1.2-2') || '-'" [isPercent]="true"
[locale]="user?.settings?.locale"
[value]="markets?.otherMarkets?.value"
></gf-value> ></gf-value>
</div> </div>
</div> </div>

37
apps/client/src/app/pages/public/public-page.component.ts

@ -7,6 +7,7 @@ import {
PortfolioPosition, PortfolioPosition,
PortfolioPublicDetails PortfolioPublicDetails
} from '@ghostfolio/common/interfaces'; } from '@ghostfolio/common/interfaces';
import { Market } from '@ghostfolio/common/types';
import { StatusCodes } from 'http-status-codes'; import { StatusCodes } from 'http-status-codes';
import { DeviceDetectorService } from 'ngx-device-detector'; import { DeviceDetectorService } from 'ngx-device-detector';
import { EMPTY, Subject } from 'rxjs'; import { EMPTY, Subject } from 'rxjs';
@ -26,6 +27,9 @@ export class PublicPageComponent implements OnInit {
[code: string]: { name: string; value: number }; [code: string]: { name: string; value: number };
}; };
public deviceType: string; public deviceType: string;
public markets: {
[key in Market]: { name: string; value: number };
};
public portfolioPublicDetails: PortfolioPublicDetails; public portfolioPublicDetails: PortfolioPublicDetails;
public positions: { public positions: {
[symbol: string]: Pick<PortfolioPosition, 'currency' | 'name' | 'value'>; [symbol: string]: Pick<PortfolioPosition, 'currency' | 'name' | 'value'>;
@ -96,6 +100,20 @@ export class PublicPageComponent implements OnInit {
value: 0 value: 0
} }
}; };
this.markets = {
developedMarkets: {
name: 'developedMarkets',
value: 0
},
emergingMarkets: {
name: 'emergingMarkets',
value: 0
},
otherMarkets: {
name: 'otherMarkets',
value: 0
}
};
this.positions = {}; this.positions = {};
this.sectors = { this.sectors = {
[UNKNOWN_KEY]: { [UNKNOWN_KEY]: {
@ -123,6 +141,13 @@ export class PublicPageComponent implements OnInit {
}; };
if (position.countries.length > 0) { if (position.countries.length > 0) {
this.markets.developedMarkets.value +=
position.markets.developedMarkets * position.value;
this.markets.emergingMarkets.value +=
position.markets.emergingMarkets * position.value;
this.markets.otherMarkets.value +=
position.markets.otherMarkets * position.value;
for (const country of position.countries) { for (const country of position.countries) {
const { code, continent, name, weight } = country; const { code, continent, name, weight } = country;
@ -176,6 +201,18 @@ export class PublicPageComponent implements OnInit {
value: position.value value: position.value
}; };
} }
const marketsTotal =
this.markets.developedMarkets.value +
this.markets.emergingMarkets.value +
this.markets.otherMarkets.value;
this.markets.developedMarkets.value =
this.markets.developedMarkets.value / marketsTotal;
this.markets.emergingMarkets.value =
this.markets.emergingMarkets.value / marketsTotal;
this.markets.otherMarkets.value =
this.markets.otherMarkets.value / marketsTotal;
} }
public ngOnDestroy() { public ngOnDestroy() {

28
apps/client/src/app/pages/public/public-page.html

@ -79,12 +79,38 @@
[countries]="countries" [countries]="countries"
[isInPercent]="true" [isInPercent]="true"
></gf-world-map-chart> ></gf-world-map-chart>
<div class="row">
<div class="col-xs-12 col-md-4 my-2">
<gf-value
label="Developed Markets"
size="large"
[isPercent]="true"
[value]="markets?.developedMarkets?.value"
></gf-value>
</div>
<div class="col-xs-12 col-md-4 my-2">
<gf-value
label="Emerging Markets"
size="large"
[isPercent]="true"
[value]="markets?.emergingMarkets?.value"
></gf-value>
</div>
<div class="col-xs-12 col-md-4 my-2">
<gf-value
label="Other Markets"
size="large"
[isPercent]="true"
[value]="markets?.otherMarkets?.value"
></gf-value>
</div>
</div>
</mat-card-content> </mat-card-content>
</mat-card> </mat-card>
</div> </div>
</div> </div>
<div class="row my-5"> <div class="row my-5">
<div class="col-md-8 offset-md-2"> <div class="col-md-10 offset-md-1">
<h2 class="h4 mb-1 text-center"> <h2 class="h4 mb-1 text-center">
Would you like to <strong>refine</strong> your Would you like to <strong>refine</strong> your
<strong>personal investment strategy</strong>? <strong>personal investment strategy</strong>?

2
apps/client/src/app/pages/public/public-page.module.ts

@ -4,6 +4,7 @@ import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card'; import { MatCardModule } from '@angular/material/card';
import { GfWorldMapChartModule } from '@ghostfolio/client/components/world-map-chart/world-map-chart.module'; import { GfWorldMapChartModule } from '@ghostfolio/client/components/world-map-chart/world-map-chart.module';
import { GfPortfolioProportionChartModule } from '@ghostfolio/ui/portfolio-proportion-chart/portfolio-proportion-chart.module'; import { GfPortfolioProportionChartModule } from '@ghostfolio/ui/portfolio-proportion-chart/portfolio-proportion-chart.module';
import { GfValueModule } from '@ghostfolio/ui/value';
import { PublicPageRoutingModule } from './public-page-routing.module'; import { PublicPageRoutingModule } from './public-page-routing.module';
import { PublicPageComponent } from './public-page.component'; import { PublicPageComponent } from './public-page.component';
@ -14,6 +15,7 @@ import { PublicPageComponent } from './public-page.component';
imports: [ imports: [
CommonModule, CommonModule,
GfPortfolioProportionChartModule, GfPortfolioProportionChartModule,
GfValueModule,
GfWorldMapChartModule, GfWorldMapChartModule,
MatButtonModule, MatButtonModule,
MatCardModule, MatCardModule,

4
apps/client/src/styles.scss

@ -184,10 +184,6 @@ ngx-skeleton-loader {
text-decoration: underline !important; text-decoration: underline !important;
} }
.width-auto {
width: auto;
}
.with-info-message { .with-info-message {
height: calc(100vh - 5rem - 3.5rem) !important; height: calc(100vh - 5rem - 3.5rem) !important;
} }

3
libs/common/src/lib/interfaces/portfolio-position.interface.ts

@ -1,5 +1,6 @@
import { MarketState } from '@ghostfolio/api/services/interfaces/interfaces'; import { MarketState } from '@ghostfolio/api/services/interfaces/interfaces';
import { AssetClass, AssetSubClass, DataSource } from '@prisma/client'; import { AssetClass, AssetSubClass, DataSource } from '@prisma/client';
import { Market } from '../types';
import { Country } from './country.interface'; import { Country } from './country.interface';
import { Sector } from './sector.interface'; import { Sector } from './sector.interface';
@ -19,7 +20,7 @@ export interface PortfolioPosition {
marketChange?: number; marketChange?: number;
marketChangePercent?: number; marketChangePercent?: number;
marketPrice: number; marketPrice: number;
markets?: { [market: string]: number }; markets?: { [key in Market]: number };
marketState: MarketState; marketState: MarketState;
name: string; name: string;
netPerformance: number; netPerformance: number;

1
libs/common/src/lib/interfaces/portfolio-public-details.interface.ts

@ -8,6 +8,7 @@ export interface PortfolioPublicDetails {
| 'allocationCurrent' | 'allocationCurrent'
| 'countries' | 'countries'
| 'currency' | 'currency'
| 'markets'
| 'name' | 'name'
| 'sectors' | 'sectors'
| 'value' | 'value'

2
libs/common/src/lib/types/index.ts

@ -2,6 +2,7 @@ import type { AccessWithGranteeUser } from './access-with-grantee-user.type';
import { AccountWithValue } from './account-with-value.type'; import { AccountWithValue } from './account-with-value.type';
import type { DateRange } from './date-range.type'; import type { DateRange } from './date-range.type';
import type { Granularity } from './granularity.type'; import type { Granularity } from './granularity.type';
import { Market } from './market.type';
import type { OrderWithAccount } from './order-with-account.type'; import type { OrderWithAccount } from './order-with-account.type';
import type { RequestWithUser } from './request-with-user.type'; import type { RequestWithUser } from './request-with-user.type';
import { ToggleOption } from './toggle-option.type'; import { ToggleOption } from './toggle-option.type';
@ -11,6 +12,7 @@ export type {
AccountWithValue, AccountWithValue,
DateRange, DateRange,
Granularity, Granularity,
Market,
OrderWithAccount, OrderWithAccount,
RequestWithUser, RequestWithUser,
ToggleOption ToggleOption

1
libs/common/src/lib/types/market.type.ts

@ -0,0 +1 @@
export type Market = 'developedMarkets' | 'emergingMarkets' | 'otherMarkets';

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

@ -21,7 +21,7 @@ export class ValueComponent implements OnChanges {
@Input() isCurrency = false; @Input() isCurrency = false;
@Input() isPercent = false; @Input() isPercent = false;
@Input() label = ''; @Input() label = '';
@Input() locale = ''; @Input() locale = 'en-US';
@Input() position = ''; @Input() position = '';
@Input() precision: number | undefined; @Input() precision: number | undefined;
@Input() size: 'large' | 'medium' | 'small' = 'small'; @Input() size: 'large' | 'medium' | 'small' = 'small';

Loading…
Cancel
Save