Feature/restructure page hierarchy (#259)
* Introduce portfolio group, remove tools * Extract allocations to separate page * Update changelogpull/263/head
parent
dfcf826b4f
commit
f743c03e17
@ -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 {}
|
@ -1,16 +1,4 @@
|
||||
:host {
|
||||
.investment-chart {
|
||||
.mat-card {
|
||||
.mat-card-content {
|
||||
aspect-ratio: 16 / 9;
|
||||
|
||||
gf-investment-chart {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.proportion-charts {
|
||||
.mat-card {
|
||||
.mat-card-content {
|
@ -0,0 +1,92 @@
|
||||
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 { 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';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
|
||||
@Component({
|
||||
selector: 'gf-analysis-page',
|
||||
templateUrl: './analysis-page.html',
|
||||
styleUrls: ['./analysis-page.scss']
|
||||
})
|
||||
export class AnalysisPageComponent 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 investments: InvestmentItem[];
|
||||
public portfolioPositions: { [symbol: string]: PortfolioPosition };
|
||||
public positions: { [symbol: string]: any };
|
||||
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
|
||||
.fetchInvestments()
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
.subscribe((response) => {
|
||||
this.investments = response;
|
||||
|
||||
this.changeDetectorRef.markForCheck();
|
||||
});
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
<div class="container">
|
||||
<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
|
||||
>Timeline</mat-card-title
|
||||
>
|
||||
</mat-card-header>
|
||||
<mat-card-content>
|
||||
<gf-investment-chart
|
||||
class="h-100"
|
||||
[investments]="investments"
|
||||
></gf-investment-chart>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,21 @@
|
||||
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 { AnalysisPageRoutingModule } from './analysis-page-routing.module';
|
||||
import { AnalysisPageComponent } from './analysis-page.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [AnalysisPageComponent],
|
||||
exports: [],
|
||||
imports: [
|
||||
AnalysisPageRoutingModule,
|
||||
CommonModule,
|
||||
GfInvestmentChartModule,
|
||||
MatCardModule
|
||||
],
|
||||
providers: [],
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||
})
|
||||
export class AnalysisPageModule {}
|
@ -0,0 +1,9 @@
|
||||
:host {
|
||||
.investment-chart {
|
||||
.mat-card {
|
||||
.mat-card-content {
|
||||
aspect-ratio: 16 / 9;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
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 OnDestroy, OnInit {
|
||||
public user: User;
|
||||
|
||||
private unsubscribeSubject = new Subject<void>();
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
public constructor(
|
||||
private changeDetectorRef: ChangeDetectorRef,
|
||||
private userService: UserService
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Initializes the controller
|
||||
*/
|
||||
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();
|
||||
}
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
<div class="container">
|
||||
<h3 class="d-flex justify-content-center mb-3" i18n>Portfolio</h3>
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-md-6">
|
||||
<mat-card class="mb-3">
|
||||
<h4 i18n>Transactions</h4>
|
||||
<p class="mb-0">Manage your transactions.</p>
|
||||
<p class="text-right">
|
||||
<a
|
||||
color="primary"
|
||||
i18n
|
||||
mat-button
|
||||
[routerLink]="['/portfolio', 'transactions']"
|
||||
>
|
||||
Open Transactions →
|
||||
</a>
|
||||
</p>
|
||||
</mat-card>
|
||||
</div>
|
||||
<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>
|
||||
<p class="text-right">
|
||||
<a
|
||||
color="primary"
|
||||
i18n
|
||||
mat-button
|
||||
[routerLink]="['/portfolio', 'allocations']"
|
||||
>
|
||||
Open Allocations →
|
||||
</a>
|
||||
</p>
|
||||
</mat-card>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<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>
|
||||
<p class="text-right">
|
||||
<a
|
||||
color="primary"
|
||||
i18n
|
||||
mat-button
|
||||
[routerLink]="['/portfolio', 'analysis']"
|
||||
>
|
||||
Open Analysis →
|
||||
</a>
|
||||
</p>
|
||||
</mat-card>
|
||||
</div>
|
||||
<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">
|
||||
Ghostfolio X-ray uses static analysis to identify potential issues and
|
||||
risks in your portfolio.
|
||||
</p>
|
||||
<p class="text-right">
|
||||
<a
|
||||
color="primary"
|
||||
i18n
|
||||
mat-button
|
||||
[routerLink]="['/portfolio', 'report']"
|
||||
>
|
||||
Open X-ray →
|
||||
</a>
|
||||
</p>
|
||||
</mat-card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -1,21 +0,0 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Subject } from 'rxjs';
|
||||
|
||||
@Component({
|
||||
selector: 'gf-tools-page',
|
||||
templateUrl: './tools-page.html',
|
||||
styleUrls: ['./tools-page.scss']
|
||||
})
|
||||
export class ToolsPageComponent implements OnInit {
|
||||
private unsubscribeSubject = new Subject<void>();
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
public constructor() {}
|
||||
|
||||
/**
|
||||
* Initializes the controller
|
||||
*/
|
||||
public ngOnInit() {}
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
<div class="container">
|
||||
<h3 class="d-flex justify-content-center mb-3" i18n>Tools</h3>
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-md-6">
|
||||
<mat-card class="mb-3">
|
||||
<h4 i18n>Analysis</h4>
|
||||
<p class="mb-0">
|
||||
Ghostfolio Analysis shows your positions and visualizes your
|
||||
portfolio.
|
||||
</p>
|
||||
<p class="text-right">
|
||||
<a
|
||||
color="primary"
|
||||
i18n
|
||||
mat-button
|
||||
[routerLink]="['/tools', 'analysis']"
|
||||
>
|
||||
Open Analysis →
|
||||
</a>
|
||||
</p>
|
||||
</mat-card>
|
||||
</div>
|
||||
<div class="col-xs-12 col-md-6">
|
||||
<mat-card class="mb-3">
|
||||
<h4 i18n>X-ray</h4>
|
||||
<p class="mb-0">
|
||||
Ghostfolio X-ray uses static analysis to identify potential issues and
|
||||
risks in your portfolio.
|
||||
</p>
|
||||
<p class="text-right">
|
||||
<a
|
||||
color="primary"
|
||||
i18n
|
||||
mat-button
|
||||
[routerLink]="['/tools', 'report']"
|
||||
>
|
||||
Open X-ray →
|
||||
</a>
|
||||
</p>
|
||||
</mat-card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
Loading…
Reference in new issue