Extract allocations to separate page

pull/259/head
Thomas 4 years ago
parent 28c43a0254
commit 39a12ee05e

@ -59,6 +59,13 @@ const routes: Routes = [
(m) => m.PortfolioPageModule
)
},
{
path: 'portfolio/allocations',
loadChildren: () =>
import('./pages/portfolio/allocations/allocations-page.module').then(
(m) => m.AllocationsPageModule
)
},
{
path: 'portfolio/analysis',
loadChildren: () =>

@ -79,7 +79,7 @@
<ion-icon name="wallet-outline" size="large"></ion-icon>
</ng-template>
<div class="container justify-content-center pb-3 px-3 positions">
<h3 class="d-flex justify-content-center mb-3" i18n>Positions</h3>
<h3 class="d-flex justify-content-center mb-3" i18n>Holdings</h3>
<div class="row">
<div class="align-items-center col">
<div class="pb-2 text-center">

@ -0,0 +1,15 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AuthGuard } from '@ghostfolio/client/core/auth.guard';
import { AllocationsPageComponent } from './allocations-page.component';
const routes: Routes = [
{ path: '', component: AllocationsPageComponent, canActivate: [AuthGuard] }
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class AllocationsPageRoutingModule {}

@ -0,0 +1,221 @@
import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
import { ToggleOption } from '@ghostfolio/client/components/toggle/interfaces/toggle-option.type';
import { DataService } from '@ghostfolio/client/services/data.service';
import { ImpersonationStorageService } from '@ghostfolio/client/services/impersonation-storage.service';
import { UserService } from '@ghostfolio/client/services/user/user.service';
import { UNKNOWN_KEY } from '@ghostfolio/common/config';
import { PortfolioPosition, User } from '@ghostfolio/common/interfaces';
import { DeviceDetectorService } from 'ngx-device-detector';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
@Component({
selector: 'gf-allocations-page',
templateUrl: './allocations-page.html',
styleUrls: ['./allocations-page.scss']
})
export class AllocationsPageComponent implements OnDestroy, OnInit {
public accounts: {
[symbol: string]: Pick<PortfolioPosition, 'name'> & { value: number };
};
public continents: {
[code: string]: { name: string; value: number };
};
public countries: {
[code: string]: { name: string; value: number };
};
public deviceType: string;
public hasImpersonationId: boolean;
public period = 'current';
public periodOptions: ToggleOption[] = [
{ label: 'Initial', value: 'original' },
{ label: 'Current', value: 'current' }
];
public portfolioPositions: { [symbol: string]: PortfolioPosition };
public positions: { [symbol: string]: any };
public positionsArray: PortfolioPosition[];
public sectors: {
[name: string]: { name: string; value: number };
};
public user: User;
private unsubscribeSubject = new Subject<void>();
/**
* @constructor
*/
public constructor(
private changeDetectorRef: ChangeDetectorRef,
private dataService: DataService,
private deviceService: DeviceDetectorService,
private impersonationStorageService: ImpersonationStorageService,
private userService: UserService
) {}
/**
* Initializes the controller
*/
public ngOnInit() {
this.deviceType = this.deviceService.getDeviceInfo().deviceType;
this.impersonationStorageService
.onChangeHasImpersonation()
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe((aId) => {
this.hasImpersonationId = !!aId;
});
this.dataService
.fetchPortfolioPositions({})
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe((response = {}) => {
this.portfolioPositions = response;
this.initializeAnalysisData(this.portfolioPositions, this.period);
this.changeDetectorRef.markForCheck();
});
this.userService.stateChanged
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe((state) => {
if (state?.user) {
this.user = state.user;
this.changeDetectorRef.markForCheck();
}
});
}
public initializeAnalysisData(
aPortfolioPositions: {
[symbol: string]: PortfolioPosition;
},
aPeriod: string
) {
this.accounts = {};
this.continents = {
[UNKNOWN_KEY]: {
name: UNKNOWN_KEY,
value: 0
}
};
this.countries = {
[UNKNOWN_KEY]: {
name: UNKNOWN_KEY,
value: 0
}
};
this.positions = {};
this.positionsArray = [];
this.sectors = {
[UNKNOWN_KEY]: {
name: UNKNOWN_KEY,
value: 0
}
};
for (const [symbol, position] of Object.entries(aPortfolioPositions)) {
this.positions[symbol] = {
currency: position.currency,
exchange: position.exchange,
type: position.type,
value:
aPeriod === 'original'
? position.allocationInvestment
: position.allocationCurrent
};
this.positionsArray.push(position);
for (const [account, { current, original }] of Object.entries(
position.accounts
)) {
if (this.accounts[account]?.value) {
this.accounts[account].value +=
aPeriod === 'original' ? original : current;
} else {
this.accounts[account] = {
name: account,
value: aPeriod === 'original' ? original : current
};
}
}
if (position.countries.length > 0) {
for (const country of position.countries) {
const { code, continent, name, weight } = country;
if (this.continents[continent]?.value) {
this.continents[continent].value += weight * position.value;
} else {
this.continents[continent] = {
name: continent,
value:
weight *
(aPeriod === 'original'
? this.portfolioPositions[symbol].investment
: this.portfolioPositions[symbol].value)
};
}
if (this.countries[code]?.value) {
this.countries[code].value += weight * position.value;
} else {
this.countries[code] = {
name,
value:
weight *
(aPeriod === 'original'
? this.portfolioPositions[symbol].investment
: this.portfolioPositions[symbol].value)
};
}
}
} else {
this.continents[UNKNOWN_KEY].value +=
aPeriod === 'original'
? this.portfolioPositions[symbol].investment
: this.portfolioPositions[symbol].value;
this.countries[UNKNOWN_KEY].value +=
aPeriod === 'original'
? this.portfolioPositions[symbol].investment
: this.portfolioPositions[symbol].value;
}
if (position.sectors.length > 0) {
for (const sector of position.sectors) {
const { name, weight } = sector;
if (this.sectors[name]?.value) {
this.sectors[name].value += weight * position.value;
} else {
this.sectors[name] = {
name,
value:
weight *
(aPeriod === 'original'
? this.portfolioPositions[symbol].investment
: this.portfolioPositions[symbol].value)
};
}
}
} else {
this.sectors[UNKNOWN_KEY].value +=
aPeriod === 'original'
? this.portfolioPositions[symbol].investment
: this.portfolioPositions[symbol].value;
}
}
}
public onChangePeriod(aValue: string) {
this.period = aValue;
this.initializeAnalysisData(this.portfolioPositions, this.period);
}
public ngOnDestroy() {
this.unsubscribeSubject.next();
this.unsubscribeSubject.complete();
}
}

@ -0,0 +1,193 @@
<div class="container">
<div class="mb-5 row">
<div class="col">
<h3 class="d-flex justify-content-center mb-3" i18n>Allocations</h3>
<gf-positions-table
class="mb-4"
[baseCurrency]="user?.settings?.baseCurrency"
[deviceType]="deviceType"
[locale]="user?.settings?.locale"
[positions]="positionsArray"
></gf-positions-table>
</div>
</div>
<div class="proportion-charts row">
<div class="col-md-6">
<mat-card class="mb-3">
<mat-card-header class="w-100">
<mat-card-title i18n>By Type</mat-card-title>
<gf-toggle
[defaultValue]="period"
[isLoading]="false"
[options]="periodOptions"
(change)="onChangePeriod($event.value)"
></gf-toggle>
</mat-card-header>
<mat-card-content>
<gf-portfolio-proportion-chart
key="type"
[baseCurrency]="user?.settings?.baseCurrency"
[isInPercent]="true"
[locale]="user?.settings?.locale"
[positions]="positions"
></gf-portfolio-proportion-chart>
</mat-card-content>
</mat-card>
</div>
<div class="col-md-6">
<mat-card class="mb-3">
<mat-card-header class="w-100">
<mat-card-title i18n>By Account</mat-card-title>
<gf-toggle
[defaultValue]="period"
[isLoading]="false"
[options]="periodOptions"
(change)="onChangePeriod($event.value)"
></gf-toggle>
</mat-card-header>
<mat-card-content>
<gf-portfolio-proportion-chart
key="name"
[baseCurrency]="user?.settings?.baseCurrency"
[isInPercent]="hasImpersonationId"
[locale]="user?.settings?.locale"
[positions]="accounts"
></gf-portfolio-proportion-chart>
</mat-card-content>
</mat-card>
</div>
<div class="col-md-6">
<mat-card class="mb-3">
<mat-card-header class="w-100">
<mat-card-title i18n>By Currency</mat-card-title>
<gf-toggle
[defaultValue]="period"
[isLoading]="false"
[options]="periodOptions"
(change)="onChangePeriod($event.value)"
></gf-toggle>
</mat-card-header>
<mat-card-content>
<gf-portfolio-proportion-chart
key="currency"
[baseCurrency]="user?.settings?.baseCurrency"
[isInPercent]="true"
[locale]="user?.settings?.locale"
[positions]="positions"
></gf-portfolio-proportion-chart>
</mat-card-content>
</mat-card>
</div>
<div class="col-md-6">
<mat-card class="mb-3">
<mat-card-header class="w-100">
<mat-card-title i18n>By Exchange</mat-card-title>
<gf-toggle
[defaultValue]="period"
[isLoading]="false"
[options]="periodOptions"
(change)="onChangePeriod($event.value)"
></gf-toggle>
</mat-card-header>
<mat-card-content>
<gf-portfolio-proportion-chart
key="exchange"
[baseCurrency]="user?.settings?.baseCurrency"
[isInPercent]="true"
[locale]="user?.settings?.locale"
[positions]="positions"
></gf-portfolio-proportion-chart>
</mat-card-content>
</mat-card>
</div>
<div class="col-md-6">
<mat-card class="mb-3">
<mat-card-header class="w-100">
<mat-card-title i18n>By Sector</mat-card-title>
<gf-toggle
[defaultValue]="period"
[isLoading]="false"
[options]="periodOptions"
(change)="onChangePeriod($event.value)"
></gf-toggle>
</mat-card-header>
<mat-card-content>
<gf-portfolio-proportion-chart
key="name"
[baseCurrency]="user?.settings?.baseCurrency"
[isInPercent]="false"
[locale]="user?.settings?.locale"
[maxItems]="10"
[positions]="sectors"
></gf-portfolio-proportion-chart>
</mat-card-content>
</mat-card>
</div>
<div class="col-md-6">
<mat-card class="mb-3">
<mat-card-header class="w-100">
<mat-card-title i18n>By Continent</mat-card-title>
<gf-toggle
[defaultValue]="period"
[isLoading]="false"
[options]="periodOptions"
(change)="onChangePeriod($event.value)"
></gf-toggle>
</mat-card-header>
<mat-card-content>
<gf-portfolio-proportion-chart
key="name"
[baseCurrency]="user?.settings?.baseCurrency"
[isInPercent]="false"
[locale]="user?.settings?.locale"
[positions]="continents"
></gf-portfolio-proportion-chart>
</mat-card-content>
</mat-card>
</div>
<div class="col-md-6">
<mat-card class="mb-3">
<mat-card-header class="w-100">
<mat-card-title i18n>By Country</mat-card-title>
<gf-toggle
[defaultValue]="period"
[isLoading]="false"
[options]="periodOptions"
(change)="onChangePeriod($event.value)"
></gf-toggle>
</mat-card-header>
<mat-card-content>
<gf-portfolio-proportion-chart
key="name"
[baseCurrency]="user?.settings?.baseCurrency"
[isInPercent]="false"
[locale]="user?.settings?.locale"
[maxItems]="10"
[positions]="countries"
></gf-portfolio-proportion-chart>
</mat-card-content>
</mat-card>
</div>
</div>
<div class="row world-map-chart">
<div class="col-lg">
<mat-card class="mb-3">
<mat-card-header class="w-100">
<mat-card-title i18n>Regions</mat-card-title>
<gf-toggle
[defaultValue]="period"
[isLoading]="false"
[options]="periodOptions"
(change)="onChangePeriod($event.value)"
></gf-toggle>
</mat-card-header>
<mat-card-content>
<gf-world-map-chart
[baseCurrency]="user?.settings?.baseCurrency"
[countries]="countries"
></gf-world-map-chart>
</mat-card-content>
</mat-card>
</div>
</div>
</div>

@ -0,0 +1,27 @@
import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { MatCardModule } from '@angular/material/card';
import { PortfolioProportionChartModule } from '@ghostfolio/client/components/portfolio-proportion-chart/portfolio-proportion-chart.module';
import { GfPositionsTableModule } from '@ghostfolio/client/components/positions-table/positions-table.module';
import { GfToggleModule } from '@ghostfolio/client/components/toggle/toggle.module';
import { GfWorldMapChartModule } from '@ghostfolio/client/components/world-map-chart/world-map-chart.module';
import { AllocationsPageRoutingModule } from './allocations-page-routing.module';
import { AllocationsPageComponent } from './allocations-page.component';
@NgModule({
declarations: [AllocationsPageComponent],
exports: [],
imports: [
AllocationsPageRoutingModule,
CommonModule,
GfPositionsTableModule,
GfToggleModule,
GfWorldMapChartModule,
MatCardModule,
PortfolioProportionChartModule
],
providers: [],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class AllocationsPageModule {}

@ -0,0 +1,40 @@
:host {
.proportion-charts {
.mat-card {
.mat-card-content {
padding: 1rem 2rem;
}
}
}
.world-map-chart {
.mat-card {
.mat-card-content {
aspect-ratio: 16 / 9;
}
}
}
.mat-card {
.mat-card-header {
::ng-deep {
.mat-card-header-text {
flex: 1 1 auto;
}
}
gf-toggle {
font-size: 90%;
}
}
a {
color: rgba(var(--palette-primary-500), 1);
font-weight: 500;
&:hover {
color: rgba(var(--palette-primary-300), 1);
}
}
}
}

@ -3,12 +3,7 @@ import { ToggleOption } from '@ghostfolio/client/components/toggle/interfaces/to
import { DataService } from '@ghostfolio/client/services/data.service';
import { ImpersonationStorageService } from '@ghostfolio/client/services/impersonation-storage.service';
import { UserService } from '@ghostfolio/client/services/user/user.service';
import { UNKNOWN_KEY } from '@ghostfolio/common/config';
import {
PortfolioItem,
PortfolioPosition,
User
} from '@ghostfolio/common/interfaces';
import { PortfolioPosition, User } from '@ghostfolio/common/interfaces';
import { InvestmentItem } from '@ghostfolio/common/interfaces/investment-item.interface';
import { DeviceDetectorService } from 'ngx-device-detector';
import { Subject } from 'rxjs';
@ -39,7 +34,6 @@ export class AnalysisPageComponent implements OnDestroy, OnInit {
public investments: InvestmentItem[];
public portfolioPositions: { [symbol: string]: PortfolioPosition };
public positions: { [symbol: string]: any };
public positionsArray: PortfolioPosition[];
public sectors: {
[name: string]: { name: string; value: number };
};
@ -80,16 +74,6 @@ export class AnalysisPageComponent implements OnDestroy, OnInit {
this.changeDetectorRef.markForCheck();
});
this.dataService
.fetchPortfolioPositions({})
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe((response = {}) => {
this.portfolioPositions = response;
this.initializeAnalysisData(this.portfolioPositions, this.period);
this.changeDetectorRef.markForCheck();
});
this.userService.stateChanged
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe((state) => {
@ -101,134 +85,6 @@ export class AnalysisPageComponent implements OnDestroy, OnInit {
});
}
public initializeAnalysisData(
aPortfolioPositions: {
[symbol: string]: PortfolioPosition;
},
aPeriod: string
) {
this.accounts = {};
this.continents = {
[UNKNOWN_KEY]: {
name: UNKNOWN_KEY,
value: 0
}
};
this.countries = {
[UNKNOWN_KEY]: {
name: UNKNOWN_KEY,
value: 0
}
};
this.positions = {};
this.positionsArray = [];
this.sectors = {
[UNKNOWN_KEY]: {
name: UNKNOWN_KEY,
value: 0
}
};
for (const [symbol, position] of Object.entries(aPortfolioPositions)) {
this.positions[symbol] = {
currency: position.currency,
exchange: position.exchange,
type: position.type,
value:
aPeriod === 'original'
? position.allocationInvestment
: position.allocationCurrent
};
this.positionsArray.push(position);
for (const [account, { current, original }] of Object.entries(
position.accounts
)) {
if (this.accounts[account]?.value) {
this.accounts[account].value +=
aPeriod === 'original' ? original : current;
} else {
this.accounts[account] = {
name: account,
value: aPeriod === 'original' ? original : current
};
}
}
if (position.countries.length > 0) {
for (const country of position.countries) {
const { code, continent, name, weight } = country;
if (this.continents[continent]?.value) {
this.continents[continent].value += weight * position.value;
} else {
this.continents[continent] = {
name: continent,
value:
weight *
(aPeriod === 'original'
? this.portfolioPositions[symbol].investment
: this.portfolioPositions[symbol].value)
};
}
if (this.countries[code]?.value) {
this.countries[code].value += weight * position.value;
} else {
this.countries[code] = {
name,
value:
weight *
(aPeriod === 'original'
? this.portfolioPositions[symbol].investment
: this.portfolioPositions[symbol].value)
};
}
}
} else {
this.continents[UNKNOWN_KEY].value +=
aPeriod === 'original'
? this.portfolioPositions[symbol].investment
: this.portfolioPositions[symbol].value;
this.countries[UNKNOWN_KEY].value +=
aPeriod === 'original'
? this.portfolioPositions[symbol].investment
: this.portfolioPositions[symbol].value;
}
if (position.sectors.length > 0) {
for (const sector of position.sectors) {
const { name, weight } = sector;
if (this.sectors[name]?.value) {
this.sectors[name].value += weight * position.value;
} else {
this.sectors[name] = {
name,
value:
weight *
(aPeriod === 'original'
? this.portfolioPositions[symbol].investment
: this.portfolioPositions[symbol].value)
};
}
}
} else {
this.sectors[UNKNOWN_KEY].value +=
aPeriod === 'original'
? this.portfolioPositions[symbol].investment
: this.portfolioPositions[symbol].value;
}
}
}
public onChangePeriod(aValue: string) {
this.period = aValue;
this.initializeAnalysisData(this.portfolioPositions, this.period);
}
public ngOnDestroy() {
this.unsubscribeSubject.next();
this.unsubscribeSubject.complete();

@ -1,199 +1,7 @@
<div class="container">
<div class="mb-5 row">
<div class="col">
<h3 class="d-flex justify-content-center mb-3" i18n>Analysis</h3>
<div class="mb-4">
<h4 class="m-0" i18n>Positions</h4>
<gf-positions-table
[baseCurrency]="user?.settings?.baseCurrency"
[deviceType]="deviceType"
[locale]="user?.settings?.locale"
[positions]="positionsArray"
></gf-positions-table>
</div>
</div>
</div>
<div class="proportion-charts row">
<div class="col-md-6">
<mat-card class="mb-3">
<mat-card-header class="w-100">
<mat-card-title i18n>By Type</mat-card-title>
<gf-toggle
[defaultValue]="period"
[isLoading]="false"
[options]="periodOptions"
(change)="onChangePeriod($event.value)"
></gf-toggle>
</mat-card-header>
<mat-card-content>
<gf-portfolio-proportion-chart
key="type"
[baseCurrency]="user?.settings?.baseCurrency"
[isInPercent]="true"
[locale]="user?.settings?.locale"
[positions]="positions"
></gf-portfolio-proportion-chart>
</mat-card-content>
</mat-card>
</div>
<div class="col-md-6">
<mat-card class="mb-3">
<mat-card-header class="w-100">
<mat-card-title i18n>By Account</mat-card-title>
<gf-toggle
[defaultValue]="period"
[isLoading]="false"
[options]="periodOptions"
(change)="onChangePeriod($event.value)"
></gf-toggle>
</mat-card-header>
<mat-card-content>
<gf-portfolio-proportion-chart
key="name"
[baseCurrency]="user?.settings?.baseCurrency"
[isInPercent]="hasImpersonationId"
[locale]="user?.settings?.locale"
[positions]="accounts"
></gf-portfolio-proportion-chart>
</mat-card-content>
</mat-card>
</div>
<div class="col-md-6">
<mat-card class="mb-3">
<mat-card-header class="w-100">
<mat-card-title i18n>By Currency</mat-card-title>
<gf-toggle
[defaultValue]="period"
[isLoading]="false"
[options]="periodOptions"
(change)="onChangePeriod($event.value)"
></gf-toggle>
</mat-card-header>
<mat-card-content>
<gf-portfolio-proportion-chart
key="currency"
[baseCurrency]="user?.settings?.baseCurrency"
[isInPercent]="true"
[locale]="user?.settings?.locale"
[positions]="positions"
></gf-portfolio-proportion-chart>
</mat-card-content>
</mat-card>
</div>
<div class="col-md-6">
<mat-card class="mb-3">
<mat-card-header class="w-100">
<mat-card-title i18n>By Exchange</mat-card-title>
<gf-toggle
[defaultValue]="period"
[isLoading]="false"
[options]="periodOptions"
(change)="onChangePeriod($event.value)"
></gf-toggle>
</mat-card-header>
<mat-card-content>
<gf-portfolio-proportion-chart
key="exchange"
[baseCurrency]="user?.settings?.baseCurrency"
[isInPercent]="true"
[locale]="user?.settings?.locale"
[positions]="positions"
></gf-portfolio-proportion-chart>
</mat-card-content>
</mat-card>
</div>
<div class="col-md-6">
<mat-card class="mb-3">
<mat-card-header class="w-100">
<mat-card-title i18n>By Sector</mat-card-title>
<gf-toggle
[defaultValue]="period"
[isLoading]="false"
[options]="periodOptions"
(change)="onChangePeriod($event.value)"
></gf-toggle>
</mat-card-header>
<mat-card-content>
<gf-portfolio-proportion-chart
key="name"
[baseCurrency]="user?.settings?.baseCurrency"
[isInPercent]="false"
[locale]="user?.settings?.locale"
[maxItems]="10"
[positions]="sectors"
></gf-portfolio-proportion-chart>
</mat-card-content>
</mat-card>
</div>
<div class="col-md-6">
<mat-card class="mb-3">
<mat-card-header class="w-100">
<mat-card-title i18n>By Continent</mat-card-title>
<gf-toggle
[defaultValue]="period"
[isLoading]="false"
[options]="periodOptions"
(change)="onChangePeriod($event.value)"
></gf-toggle>
</mat-card-header>
<mat-card-content>
<gf-portfolio-proportion-chart
key="name"
[baseCurrency]="user?.settings?.baseCurrency"
[isInPercent]="false"
[locale]="user?.settings?.locale"
[positions]="continents"
></gf-portfolio-proportion-chart>
</mat-card-content>
</mat-card>
</div>
<div class="col-md-6">
<mat-card class="mb-3">
<mat-card-header class="w-100">
<mat-card-title i18n>By Country</mat-card-title>
<gf-toggle
[defaultValue]="period"
[isLoading]="false"
[options]="periodOptions"
(change)="onChangePeriod($event.value)"
></gf-toggle>
</mat-card-header>
<mat-card-content>
<gf-portfolio-proportion-chart
key="name"
[baseCurrency]="user?.settings?.baseCurrency"
[isInPercent]="false"
[locale]="user?.settings?.locale"
[maxItems]="10"
[positions]="countries"
></gf-portfolio-proportion-chart>
</mat-card-content>
</mat-card>
</div>
</div>
<div class="row world-map-chart">
<div class="col-lg">
<mat-card class="mb-3">
<mat-card-header class="w-100">
<mat-card-title i18n>Regions</mat-card-title>
<gf-toggle
[defaultValue]="period"
[isLoading]="false"
[options]="periodOptions"
(change)="onChangePeriod($event.value)"
></gf-toggle>
</mat-card-header>
<mat-card-content>
<gf-world-map-chart
[baseCurrency]="user?.settings?.baseCurrency"
[countries]="countries"
></gf-world-map-chart>
</mat-card-content>
</mat-card>
</div>
</div>
<div class="investment-chart row">
<div class="col-lg">
<h3 class="d-flex justify-content-center mb-3" i18n>Analysis</h3>
<mat-card class="mb-3">
<mat-card-header>
<mat-card-title class="align-items-center d-flex" i18n
@ -202,7 +10,7 @@
</mat-card-header>
<mat-card-content>
<gf-investment-chart
[investments]='investments'
[investments]="investments"
></gf-investment-chart>
</mat-card-content>
</mat-card>

@ -2,10 +2,6 @@ import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { MatCardModule } from '@angular/material/card';
import { GfInvestmentChartModule } from '@ghostfolio/client/components/investment-chart/investment-chart.module';
import { PortfolioProportionChartModule } from '@ghostfolio/client/components/portfolio-proportion-chart/portfolio-proportion-chart.module';
import { GfPositionsTableModule } from '@ghostfolio/client/components/positions-table/positions-table.module';
import { GfToggleModule } from '@ghostfolio/client/components/toggle/toggle.module';
import { GfWorldMapChartModule } from '@ghostfolio/client/components/world-map-chart/world-map-chart.module';
import { AnalysisPageRoutingModule } from './analysis-page-routing.module';
import { AnalysisPageComponent } from './analysis-page.component';
@ -17,11 +13,7 @@ import { AnalysisPageComponent } from './analysis-page.component';
AnalysisPageRoutingModule,
CommonModule,
GfInvestmentChartModule,
GfPositionsTableModule,
GfToggleModule,
GfWorldMapChartModule,
MatCardModule,
PortfolioProportionChartModule
MatCardModule
],
providers: [],
schemas: [CUSTOM_ELEMENTS_SCHEMA]

@ -11,22 +11,6 @@
}
}
.proportion-charts {
.mat-card {
.mat-card-content {
padding: 1rem 2rem;
}
}
}
.world-map-chart {
.mat-card {
.mat-card-content {
aspect-ratio: 16 / 9;
}
}
}
.mat-card {
.mat-card-header {
::ng-deep {

@ -1,18 +1,44 @@
import { Component, OnInit } from '@angular/core';
import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
import { UserService } from '@ghostfolio/client/services/user/user.service';
import { User } from '@ghostfolio/common/interfaces';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
@Component({
selector: 'gf-portfolio-page',
templateUrl: './portfolio-page.html',
styleUrls: ['./portfolio-page.scss']
})
export class PortfolioPageComponent implements OnInit {
export class PortfolioPageComponent implements OnDestroy, OnInit {
public user: User;
private unsubscribeSubject = new Subject<void>();
/**
* @constructor
*/
public constructor() {}
public constructor(
private changeDetectorRef: ChangeDetectorRef,
private userService: UserService
) {}
/**
* Initializes the controller
*/
public ngOnInit() {}
public ngOnInit() {
this.userService.stateChanged
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe((state) => {
if (state?.user) {
this.user = state.user;
this.changeDetectorRef.markForCheck();
}
});
}
public ngOnDestroy() {
this.unsubscribeSubject.next();
this.unsubscribeSubject.complete();
}
}

@ -17,7 +17,10 @@
</p>
</mat-card>
</div>
<div class="col-xs-12 col-md-6">
<div
*ngIf="user?.settings?.viewMode === 'DEFAULT'"
class="col-xs-12 col-md-6"
>
<mat-card class="mb-3">
<h4 i18n>Allocations</h4>
<p class="mb-0">Check the allocations of your portfolio.</p>
@ -35,7 +38,10 @@
</div>
</div>
<div class="row">
<div class="col-xs-12 col-md-6">
<div
*ngIf="user?.settings?.viewMode === 'DEFAULT'"
class="col-xs-12 col-md-6"
>
<mat-card class="mb-3">
<h4 i18n>Analysis</h4>
<p class="mb-0">Ghostfolio Analysis visualizes your portfolio.</p>
@ -51,7 +57,10 @@
</p>
</mat-card>
</div>
<div class="col-xs-12 col-md-6">
<div
*ngIf="user?.settings?.viewMode === 'DEFAULT'"
class="col-xs-12 col-md-6"
>
<mat-card class="mb-3">
<h4 i18n>X-ray</h4>
<p class="mb-0">

@ -9,6 +9,7 @@ import { FormControl, Validators } from '@angular/forms';
import { MatAutocompleteSelectedEvent } from '@angular/material/autocomplete';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { LookupItem } from '@ghostfolio/api/app/symbol/interfaces/lookup-item.interface';
import { DataService } from '@ghostfolio/client/services/data.service';
import { Currency } from '@prisma/client';
import { Observable, Subject } from 'rxjs';
import {
@ -19,7 +20,6 @@ import {
takeUntil
} from 'rxjs/operators';
import { DataService } from '@ghostfolio/client/services/data.service';
import { CreateOrUpdateTransactionDialogParams } from './interfaces/interfaces';
@Component({

@ -60,7 +60,7 @@
<ion-icon name="wallet-outline" size="large"></ion-icon>
</ng-template>
<div class="container justify-content-center pb-3 px-3 positions">
<h3 class="d-flex justify-content-center mb-3" i18n>Positions</h3>
<h3 class="d-flex justify-content-center mb-3" i18n>Holdings</h3>
<div class="row">
<div class="align-items-center col">
<mat-card *ngIf="hasPositions === true" class="p-0">

Loading…
Cancel
Save