mirror of https://github.com/ghostfolio/ghostfolio
Browse Source
* Introduce portfolio group, remove tools * Extract allocations to separate page * Update changelogpull/263/head
Thomas
4 years ago
committed by
GitHub
39 changed files with 366 additions and 187 deletions
@ -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 { |
:host { |
||||
.investment-chart { |
|
||||
.mat-card { |
|
||||
.mat-card-content { |
|
||||
aspect-ratio: 16 / 9; |
|
||||
|
|
||||
gf-investment-chart { |
|
||||
height: 100%; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.proportion-charts { |
.proportion-charts { |
||||
.mat-card { |
.mat-card { |
||||
.mat-card-content { |
.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