mirror of https://github.com/ghostfolio/ghostfolio
committed by
GitHub
27 changed files with 739 additions and 705 deletions
@ -0,0 +1,84 @@ |
|||||
|
import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; |
||||
|
import { DataService } from '@ghostfolio/client/services/data.service'; |
||||
|
import { |
||||
|
RANGE, |
||||
|
SettingsStorageService |
||||
|
} from '@ghostfolio/client/services/settings-storage.service'; |
||||
|
import { UserService } from '@ghostfolio/client/services/user/user.service'; |
||||
|
import { Position, User } from '@ghostfolio/common/interfaces'; |
||||
|
import { hasPermission, permissions } from '@ghostfolio/common/permissions'; |
||||
|
import { DateRange } from '@ghostfolio/common/types'; |
||||
|
import { DeviceDetectorService } from 'ngx-device-detector'; |
||||
|
import { Subject } from 'rxjs'; |
||||
|
import { takeUntil } from 'rxjs/operators'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'gf-home-holdings', |
||||
|
styleUrls: ['./home-holdings.scss'], |
||||
|
templateUrl: './home-holdings.html' |
||||
|
}) |
||||
|
export class HomeHoldingsComponent implements OnDestroy, OnInit { |
||||
|
public dateRange: DateRange; |
||||
|
public deviceType: string; |
||||
|
public hasPermissionToCreateOrder: boolean; |
||||
|
public positions: Position[]; |
||||
|
public user: User; |
||||
|
|
||||
|
private unsubscribeSubject = new Subject<void>(); |
||||
|
|
||||
|
/** |
||||
|
* @constructor |
||||
|
*/ |
||||
|
public constructor( |
||||
|
private changeDetectorRef: ChangeDetectorRef, |
||||
|
private dataService: DataService, |
||||
|
private deviceService: DeviceDetectorService, |
||||
|
private settingsStorageService: SettingsStorageService, |
||||
|
private userService: UserService |
||||
|
) { |
||||
|
this.userService.stateChanged |
||||
|
.pipe(takeUntil(this.unsubscribeSubject)) |
||||
|
.subscribe((state) => { |
||||
|
if (state?.user) { |
||||
|
this.user = state.user; |
||||
|
|
||||
|
this.hasPermissionToCreateOrder = hasPermission( |
||||
|
this.user.permissions, |
||||
|
permissions.createOrder |
||||
|
); |
||||
|
|
||||
|
this.changeDetectorRef.markForCheck(); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Initializes the controller |
||||
|
*/ |
||||
|
public ngOnInit() { |
||||
|
this.deviceType = this.deviceService.getDeviceInfo().deviceType; |
||||
|
|
||||
|
this.dateRange = |
||||
|
<DateRange>this.settingsStorageService.getSetting(RANGE) || 'max'; |
||||
|
|
||||
|
this.update(); |
||||
|
} |
||||
|
|
||||
|
public ngOnDestroy() { |
||||
|
this.unsubscribeSubject.next(); |
||||
|
this.unsubscribeSubject.complete(); |
||||
|
} |
||||
|
|
||||
|
private update() { |
||||
|
this.dataService |
||||
|
.fetchPositions({ range: this.dateRange }) |
||||
|
.pipe(takeUntil(this.unsubscribeSubject)) |
||||
|
.subscribe((response) => { |
||||
|
this.positions = response.positions; |
||||
|
|
||||
|
this.changeDetectorRef.markForCheck(); |
||||
|
}); |
||||
|
|
||||
|
this.changeDetectorRef.markForCheck(); |
||||
|
} |
||||
|
} |
@ -0,0 +1,26 @@ |
|||||
|
<div class="container justify-content-center pb-3 px-3"> |
||||
|
<div class="row"> |
||||
|
<div class="align-items-center col-xs-12 col-md-8 offset-md-2"> |
||||
|
<mat-card class="p-0"> |
||||
|
<mat-card-content> |
||||
|
<gf-positions |
||||
|
[baseCurrency]="user?.settings?.baseCurrency" |
||||
|
[deviceType]="deviceType" |
||||
|
[locale]="user?.settings?.locale" |
||||
|
[positions]="positions" |
||||
|
[range]="dateRange" |
||||
|
></gf-positions> |
||||
|
</mat-card-content> |
||||
|
</mat-card> |
||||
|
<div *ngIf="hasPermissionToCreateOrder" class="text-center"> |
||||
|
<a |
||||
|
class="mt-3" |
||||
|
i18n |
||||
|
mat-button |
||||
|
[routerLink]="['/portfolio', 'transactions']" |
||||
|
>Manage Transactions...</a |
||||
|
> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
@ -0,0 +1,23 @@ |
|||||
|
import { CommonModule } from '@angular/common'; |
||||
|
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; |
||||
|
import { MatButtonModule } from '@angular/material/button'; |
||||
|
import { MatCardModule } from '@angular/material/card'; |
||||
|
import { RouterModule } from '@angular/router'; |
||||
|
import { GfPositionsModule } from '@ghostfolio/client/components/positions/positions.module'; |
||||
|
|
||||
|
import { HomeHoldingsComponent } from './home-holdings.component'; |
||||
|
|
||||
|
@NgModule({ |
||||
|
declarations: [HomeHoldingsComponent], |
||||
|
exports: [], |
||||
|
imports: [ |
||||
|
CommonModule, |
||||
|
GfPositionsModule, |
||||
|
MatButtonModule, |
||||
|
MatCardModule, |
||||
|
RouterModule |
||||
|
], |
||||
|
providers: [], |
||||
|
schemas: [CUSTOM_ELEMENTS_SCHEMA] |
||||
|
}) |
||||
|
export class GfHomeHoldingsModule {} |
@ -0,0 +1,5 @@ |
|||||
|
@import '~apps/client/src/styles/ghostfolio-style'; |
||||
|
|
||||
|
:host { |
||||
|
display: block; |
||||
|
} |
@ -0,0 +1,74 @@ |
|||||
|
import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; |
||||
|
import { DataService } from '@ghostfolio/client/services/data.service'; |
||||
|
import { UserService } from '@ghostfolio/client/services/user/user.service'; |
||||
|
import { ghostfolioFearAndGreedIndexSymbol } from '@ghostfolio/common/config'; |
||||
|
import { User } from '@ghostfolio/common/interfaces'; |
||||
|
import { hasPermission, permissions } from '@ghostfolio/common/permissions'; |
||||
|
import { DataSource } from '@prisma/client'; |
||||
|
import { Subject } from 'rxjs'; |
||||
|
import { takeUntil } from 'rxjs/operators'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'gf-home-market', |
||||
|
styleUrls: ['./home-market.scss'], |
||||
|
templateUrl: './home-market.html' |
||||
|
}) |
||||
|
export class HomeMarketComponent implements OnDestroy, OnInit { |
||||
|
public fearAndGreedIndex: number; |
||||
|
public hasPermissionToAccessFearAndGreedIndex: boolean; |
||||
|
public isLoading = true; |
||||
|
public user: User; |
||||
|
|
||||
|
private unsubscribeSubject = new Subject<void>(); |
||||
|
|
||||
|
/** |
||||
|
* @constructor |
||||
|
*/ |
||||
|
public constructor( |
||||
|
private changeDetectorRef: ChangeDetectorRef, |
||||
|
private dataService: DataService, |
||||
|
private userService: UserService |
||||
|
) { |
||||
|
this.isLoading = true; |
||||
|
|
||||
|
this.userService.stateChanged |
||||
|
.pipe(takeUntil(this.unsubscribeSubject)) |
||||
|
.subscribe((state) => { |
||||
|
if (state?.user) { |
||||
|
this.user = state.user; |
||||
|
|
||||
|
this.hasPermissionToAccessFearAndGreedIndex = hasPermission( |
||||
|
this.user.permissions, |
||||
|
permissions.accessFearAndGreedIndex |
||||
|
); |
||||
|
|
||||
|
if (this.hasPermissionToAccessFearAndGreedIndex) { |
||||
|
this.dataService |
||||
|
.fetchSymbolItem({ |
||||
|
dataSource: DataSource.RAKUTEN, |
||||
|
symbol: ghostfolioFearAndGreedIndexSymbol |
||||
|
}) |
||||
|
.pipe(takeUntil(this.unsubscribeSubject)) |
||||
|
.subscribe(({ marketPrice }) => { |
||||
|
this.fearAndGreedIndex = marketPrice; |
||||
|
this.isLoading = false; |
||||
|
|
||||
|
this.changeDetectorRef.markForCheck(); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
this.changeDetectorRef.markForCheck(); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Initializes the controller |
||||
|
*/ |
||||
|
public ngOnInit() {} |
||||
|
|
||||
|
public ngOnDestroy() { |
||||
|
this.unsubscribeSubject.next(); |
||||
|
this.unsubscribeSubject.complete(); |
||||
|
} |
||||
|
} |
@ -0,0 +1,25 @@ |
|||||
|
<div |
||||
|
class=" |
||||
|
align-items-center |
||||
|
container |
||||
|
d-flex |
||||
|
flex-grow-1 |
||||
|
h-100 |
||||
|
justify-content-center |
||||
|
w-100 |
||||
|
" |
||||
|
> |
||||
|
<div class="row w-100"> |
||||
|
<div class="col-xs-12 col-md-8 offset-md-2"> |
||||
|
<mat-card class="h-100"> |
||||
|
<mat-card-content> |
||||
|
<gf-fear-and-greed-index |
||||
|
class="d-flex justify-content-center" |
||||
|
[fearAndGreedIndex]="fearAndGreedIndex" |
||||
|
[hidden]="isLoading" |
||||
|
></gf-fear-and-greed-index> |
||||
|
</mat-card-content> |
||||
|
</mat-card> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
@ -0,0 +1,15 @@ |
|||||
|
import { CommonModule } from '@angular/common'; |
||||
|
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; |
||||
|
import { MatCardModule } from '@angular/material/card'; |
||||
|
import { GfFearAndGreedIndexModule } from '@ghostfolio/client/components/fear-and-greed-index/fear-and-greed-index.module'; |
||||
|
|
||||
|
import { HomeMarketComponent } from './home-market.component'; |
||||
|
|
||||
|
@NgModule({ |
||||
|
declarations: [HomeMarketComponent], |
||||
|
exports: [], |
||||
|
imports: [CommonModule, GfFearAndGreedIndexModule, MatCardModule], |
||||
|
providers: [], |
||||
|
schemas: [CUSTOM_ELEMENTS_SCHEMA] |
||||
|
}) |
||||
|
export class GfHomeMarketModule {} |
@ -0,0 +1,5 @@ |
|||||
|
@import '~apps/client/src/styles/ghostfolio-style'; |
||||
|
|
||||
|
:host { |
||||
|
display: block; |
||||
|
} |
@ -0,0 +1,122 @@ |
|||||
|
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 { |
||||
|
RANGE, |
||||
|
SettingsStorageService |
||||
|
} from '@ghostfolio/client/services/settings-storage.service'; |
||||
|
import { UserService } from '@ghostfolio/client/services/user/user.service'; |
||||
|
import { PortfolioPerformance, User } from '@ghostfolio/common/interfaces'; |
||||
|
import { DateRange } from '@ghostfolio/common/types'; |
||||
|
import { LineChartItem } from '@ghostfolio/ui/line-chart/interfaces/line-chart.interface'; |
||||
|
import { Subject } from 'rxjs'; |
||||
|
import { takeUntil } from 'rxjs/operators'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'gf-home-overview', |
||||
|
styleUrls: ['./home-overview.scss'], |
||||
|
templateUrl: './home-overview.html' |
||||
|
}) |
||||
|
export class HomeOverviewComponent implements OnDestroy, OnInit { |
||||
|
public dateRange: DateRange; |
||||
|
public dateRangeOptions: ToggleOption[] = [ |
||||
|
{ label: 'Today', value: '1d' }, |
||||
|
{ label: 'YTD', value: 'ytd' }, |
||||
|
{ label: '1Y', value: '1y' }, |
||||
|
{ label: '5Y', value: '5y' }, |
||||
|
{ label: 'Max', value: 'max' } |
||||
|
]; |
||||
|
public hasImpersonationId: boolean; |
||||
|
public historicalDataItems: LineChartItem[]; |
||||
|
public isAllTimeHigh: boolean; |
||||
|
public isAllTimeLow: boolean; |
||||
|
public isLoadingPerformance = true; |
||||
|
public performance: PortfolioPerformance; |
||||
|
public user: User; |
||||
|
|
||||
|
private unsubscribeSubject = new Subject<void>(); |
||||
|
|
||||
|
/** |
||||
|
* @constructor |
||||
|
*/ |
||||
|
public constructor( |
||||
|
private changeDetectorRef: ChangeDetectorRef, |
||||
|
private dataService: DataService, |
||||
|
private impersonationStorageService: ImpersonationStorageService, |
||||
|
private settingsStorageService: SettingsStorageService, |
||||
|
private userService: UserService |
||||
|
) { |
||||
|
this.userService.stateChanged |
||||
|
.pipe(takeUntil(this.unsubscribeSubject)) |
||||
|
.subscribe((state) => { |
||||
|
if (state?.user) { |
||||
|
this.user = state.user; |
||||
|
|
||||
|
this.changeDetectorRef.markForCheck(); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Initializes the controller |
||||
|
*/ |
||||
|
public ngOnInit() { |
||||
|
this.impersonationStorageService |
||||
|
.onChangeHasImpersonation() |
||||
|
.pipe(takeUntil(this.unsubscribeSubject)) |
||||
|
.subscribe((aId) => { |
||||
|
this.hasImpersonationId = !!aId; |
||||
|
|
||||
|
this.changeDetectorRef.markForCheck(); |
||||
|
}); |
||||
|
|
||||
|
this.dateRange = |
||||
|
<DateRange>this.settingsStorageService.getSetting(RANGE) || 'max'; |
||||
|
|
||||
|
this.update(); |
||||
|
} |
||||
|
|
||||
|
public onChangeDateRange(aDateRange: DateRange) { |
||||
|
this.dateRange = aDateRange; |
||||
|
this.settingsStorageService.setSetting(RANGE, this.dateRange); |
||||
|
this.update(); |
||||
|
} |
||||
|
|
||||
|
public ngOnDestroy() { |
||||
|
this.unsubscribeSubject.next(); |
||||
|
this.unsubscribeSubject.complete(); |
||||
|
} |
||||
|
|
||||
|
private update() { |
||||
|
this.isLoadingPerformance = true; |
||||
|
|
||||
|
this.dataService |
||||
|
.fetchChart({ range: this.dateRange }) |
||||
|
.pipe(takeUntil(this.unsubscribeSubject)) |
||||
|
.subscribe((chartData) => { |
||||
|
this.historicalDataItems = chartData.chart.map((chartDataItem) => { |
||||
|
return { |
||||
|
date: chartDataItem.date, |
||||
|
value: chartDataItem.value |
||||
|
}; |
||||
|
}); |
||||
|
this.isAllTimeHigh = chartData.isAllTimeHigh; |
||||
|
this.isAllTimeLow = chartData.isAllTimeLow; |
||||
|
|
||||
|
this.changeDetectorRef.markForCheck(); |
||||
|
}); |
||||
|
|
||||
|
this.dataService |
||||
|
.fetchPortfolioPerformance({ range: this.dateRange }) |
||||
|
.pipe(takeUntil(this.unsubscribeSubject)) |
||||
|
.subscribe((response) => { |
||||
|
this.performance = response; |
||||
|
this.isLoadingPerformance = false; |
||||
|
|
||||
|
this.changeDetectorRef.markForCheck(); |
||||
|
}); |
||||
|
|
||||
|
this.changeDetectorRef.markForCheck(); |
||||
|
} |
||||
|
} |
@ -0,0 +1,55 @@ |
|||||
|
<div |
||||
|
class=" |
||||
|
align-items-center |
||||
|
container |
||||
|
d-flex |
||||
|
flex-column |
||||
|
h-100 |
||||
|
justify-content-center |
||||
|
overview |
||||
|
position-relative |
||||
|
" |
||||
|
> |
||||
|
<div class="row w-100"> |
||||
|
<div class="chart-container col"> |
||||
|
<gf-line-chart |
||||
|
symbol="Performance" |
||||
|
[historicalDataItems]="historicalDataItems" |
||||
|
[showGradient]="true" |
||||
|
[showLoader]="false" |
||||
|
[showXAxis]="false" |
||||
|
[showYAxis]="false" |
||||
|
></gf-line-chart> |
||||
|
<div |
||||
|
*ngIf="historicalDataItems?.length === 0" |
||||
|
class="align-items-center d-flex h-100 justify-content-center w-100" |
||||
|
> |
||||
|
<div class="d-flex justify-content-center"> |
||||
|
<gf-no-transactions-info-indicator></gf-no-transactions-info-indicator> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="overview-container row mt-1"> |
||||
|
<div class="col"> |
||||
|
<gf-portfolio-performance |
||||
|
class="pb-4" |
||||
|
[baseCurrency]="user?.settings?.baseCurrency" |
||||
|
[isAllTimeHigh]="isAllTimeHigh" |
||||
|
[isAllTimeLow]="isAllTimeLow" |
||||
|
[isLoading]="isLoadingPerformance" |
||||
|
[locale]="user?.settings?.locale" |
||||
|
[performance]="performance" |
||||
|
[showDetails]="!hasImpersonationId && !user.settings.isRestrictedView" |
||||
|
></gf-portfolio-performance> |
||||
|
<div class="text-center"> |
||||
|
<gf-toggle |
||||
|
[defaultValue]="dateRange" |
||||
|
[isLoading]="isLoadingPerformance" |
||||
|
[options]="dateRangeOptions" |
||||
|
(change)="onChangeDateRange($event.value)" |
||||
|
></gf-toggle> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
@ -0,0 +1,25 @@ |
|||||
|
import { CommonModule } from '@angular/common'; |
||||
|
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; |
||||
|
import { RouterModule } from '@angular/router'; |
||||
|
import { GfPortfolioPerformanceModule } from '@ghostfolio/client/components/portfolio-performance/portfolio-performance.module'; |
||||
|
import { GfToggleModule } from '@ghostfolio/client/components/toggle/toggle.module'; |
||||
|
import { GfLineChartModule } from '@ghostfolio/ui/line-chart/line-chart.module'; |
||||
|
import { GfNoTransactionsInfoModule } from '@ghostfolio/ui/no-transactions-info'; |
||||
|
|
||||
|
import { HomeOverviewComponent } from './home-overview.component'; |
||||
|
|
||||
|
@NgModule({ |
||||
|
declarations: [HomeOverviewComponent], |
||||
|
exports: [], |
||||
|
imports: [ |
||||
|
CommonModule, |
||||
|
GfLineChartModule, |
||||
|
GfNoTransactionsInfoModule, |
||||
|
GfPortfolioPerformanceModule, |
||||
|
GfToggleModule, |
||||
|
RouterModule |
||||
|
], |
||||
|
providers: [], |
||||
|
schemas: [CUSTOM_ELEMENTS_SCHEMA] |
||||
|
}) |
||||
|
export class GfHomeOverviewModule {} |
@ -0,0 +1,34 @@ |
|||||
|
@import '~apps/client/src/styles/ghostfolio-style'; |
||||
|
|
||||
|
:host { |
||||
|
display: block; |
||||
|
|
||||
|
.chart-container { |
||||
|
aspect-ratio: 16 / 9; |
||||
|
max-height: 50vh; |
||||
|
|
||||
|
// Fallback for aspect-ratio (using padding hack) |
||||
|
@supports not (aspect-ratio: 16 / 9) { |
||||
|
&::before { |
||||
|
float: left; |
||||
|
padding-top: 56.25%; |
||||
|
content: ''; |
||||
|
} |
||||
|
|
||||
|
&::after { |
||||
|
display: block; |
||||
|
content: ''; |
||||
|
clear: both; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
gf-line-chart { |
||||
|
bottom: 0; |
||||
|
left: 0; |
||||
|
position: absolute; |
||||
|
right: 0; |
||||
|
top: 0; |
||||
|
z-index: -1; |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,66 @@ |
|||||
|
import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; |
||||
|
import { DataService } from '@ghostfolio/client/services/data.service'; |
||||
|
import { UserService } from '@ghostfolio/client/services/user/user.service'; |
||||
|
import { PortfolioSummary, User } from '@ghostfolio/common/interfaces'; |
||||
|
import { Subject } from 'rxjs'; |
||||
|
import { takeUntil } from 'rxjs/operators'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'gf-home-summary', |
||||
|
styleUrls: ['./home-summary.scss'], |
||||
|
templateUrl: './home-summary.html' |
||||
|
}) |
||||
|
export class HomeSummaryComponent implements OnDestroy, OnInit { |
||||
|
public isLoading = true; |
||||
|
public summary: PortfolioSummary; |
||||
|
public user: User; |
||||
|
|
||||
|
private unsubscribeSubject = new Subject<void>(); |
||||
|
|
||||
|
/** |
||||
|
* @constructor |
||||
|
*/ |
||||
|
public constructor( |
||||
|
private changeDetectorRef: ChangeDetectorRef, |
||||
|
private dataService: DataService, |
||||
|
private userService: UserService |
||||
|
) { |
||||
|
this.userService.stateChanged |
||||
|
.pipe(takeUntil(this.unsubscribeSubject)) |
||||
|
.subscribe((state) => { |
||||
|
if (state?.user) { |
||||
|
this.user = state.user; |
||||
|
|
||||
|
this.changeDetectorRef.markForCheck(); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Initializes the controller |
||||
|
*/ |
||||
|
public ngOnInit() { |
||||
|
this.update(); |
||||
|
} |
||||
|
|
||||
|
public ngOnDestroy() { |
||||
|
this.unsubscribeSubject.next(); |
||||
|
this.unsubscribeSubject.complete(); |
||||
|
} |
||||
|
|
||||
|
private update() { |
||||
|
this.isLoading = true; |
||||
|
|
||||
|
this.dataService |
||||
|
.fetchPortfolioSummary() |
||||
|
.pipe(takeUntil(this.unsubscribeSubject)) |
||||
|
.subscribe((response) => { |
||||
|
this.summary = response; |
||||
|
this.isLoading = false; |
||||
|
|
||||
|
this.changeDetectorRef.markForCheck(); |
||||
|
}); |
||||
|
|
||||
|
this.changeDetectorRef.markForCheck(); |
||||
|
} |
||||
|
} |
@ -0,0 +1,19 @@ |
|||||
|
<div class="container pb-3 px-3"> |
||||
|
<div class="row"> |
||||
|
<div class="col-xs-12 col-md-8 offset-md-2"> |
||||
|
<mat-card class="h-100"> |
||||
|
<mat-card-header> |
||||
|
<mat-card-title i18n>Summary</mat-card-title> |
||||
|
</mat-card-header> |
||||
|
<mat-card-content> |
||||
|
<gf-portfolio-summary |
||||
|
[baseCurrency]="user?.settings?.baseCurrency" |
||||
|
[isLoading]="isLoading" |
||||
|
[locale]="user?.settings?.locale" |
||||
|
[summary]="summary" |
||||
|
></gf-portfolio-summary> |
||||
|
</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 { RouterModule } from '@angular/router'; |
||||
|
import { GfPortfolioSummaryModule } from '@ghostfolio/client/components/portfolio-summary/portfolio-summary.module'; |
||||
|
|
||||
|
import { HomeSummaryComponent } from './home-summary.component'; |
||||
|
|
||||
|
@NgModule({ |
||||
|
declarations: [HomeSummaryComponent], |
||||
|
exports: [], |
||||
|
imports: [ |
||||
|
CommonModule, |
||||
|
GfPortfolioSummaryModule, |
||||
|
MatCardModule, |
||||
|
RouterModule |
||||
|
], |
||||
|
providers: [], |
||||
|
schemas: [CUSTOM_ELEMENTS_SCHEMA] |
||||
|
}) |
||||
|
export class GfHomeSummaryModule {} |
@ -0,0 +1,5 @@ |
|||||
|
@import '~apps/client/src/styles/ghostfolio-style'; |
||||
|
|
||||
|
:host { |
||||
|
display: block; |
||||
|
} |
@ -1,169 +1,14 @@ |
|||||
<mat-tab-group |
<router-outlet></router-outlet> |
||||
animationDuration="0ms" |
|
||||
class="position-absolute" |
<nav mat-align-tabs="center" mat-tab-nav-bar> |
||||
headerPosition="below" |
<a |
||||
mat-align-tabs="center" |
*ngFor="let tab of tabs" |
||||
[disablePagination]="true" |
#rla="routerLinkActive" |
||||
(selectedTabChange)="onTabChanged($event)" |
mat-tab-link |
||||
> |
routerLinkActive |
||||
<mat-tab> |
[active]="rla.isActive" |
||||
<ng-template mat-tab-label> |
[routerLink]="tab.path" |
||||
<ion-icon name="analytics-outline" size="large"></ion-icon> |
> |
||||
</ng-template> |
<ion-icon size="large" [name]="tab.iconName"></ion-icon> |
||||
<div |
</a> |
||||
class=" |
</nav> |
||||
align-items-center |
|
||||
container |
|
||||
d-flex |
|
||||
flex-column |
|
||||
h-100 |
|
||||
justify-content-center |
|
||||
overview |
|
||||
position-relative |
|
||||
" |
|
||||
> |
|
||||
<div class="row w-100"> |
|
||||
<div class="chart-container col"> |
|
||||
<gf-line-chart |
|
||||
class="mr-3" |
|
||||
symbol="Performance" |
|
||||
[historicalDataItems]="historicalDataItems" |
|
||||
[showGradient]="true" |
|
||||
[showLoader]="false" |
|
||||
[showXAxis]="false" |
|
||||
[showYAxis]="false" |
|
||||
></gf-line-chart> |
|
||||
<div |
|
||||
*ngIf="historicalDataItems?.length === 0" |
|
||||
class=" |
|
||||
align-items-center |
|
||||
chart-container |
|
||||
d-flex |
|
||||
justify-content-center |
|
||||
w-100 |
|
||||
" |
|
||||
> |
|
||||
<div class="d-flex justify-content-center"> |
|
||||
<gf-no-transactions-info-indicator></gf-no-transactions-info-indicator> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
<div class="overview-container row mt-1"> |
|
||||
<div class="col"> |
|
||||
<gf-portfolio-performance |
|
||||
class="pb-4" |
|
||||
[baseCurrency]="user?.settings?.baseCurrency" |
|
||||
[isAllTimeHigh]="isAllTimeHigh" |
|
||||
[isAllTimeLow]="isAllTimeLow" |
|
||||
[isLoading]="isLoadingPerformance" |
|
||||
[locale]="user?.settings?.locale" |
|
||||
[performance]="performance" |
|
||||
[showDetails]="!hasImpersonationId && !user.settings.isRestrictedView" |
|
||||
></gf-portfolio-performance> |
|
||||
<div class="text-center"> |
|
||||
<gf-toggle |
|
||||
[defaultValue]="dateRange" |
|
||||
[isLoading]="isLoadingPerformance" |
|
||||
[options]="dateRangeOptions" |
|
||||
(change)="onChangeDateRange($event.value)" |
|
||||
></gf-toggle> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</mat-tab> |
|
||||
<mat-tab> |
|
||||
<ng-template mat-tab-label> |
|
||||
<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>Holdings</h3> |
|
||||
<div class="row"> |
|
||||
<div class="align-items-center col-xs-12 col-md-8 offset-md-2"> |
|
||||
<div class="pb-2 text-center"> |
|
||||
<gf-toggle |
|
||||
[defaultValue]="dateRange" |
|
||||
[isLoading]="isLoadingPerformance" |
|
||||
[options]="dateRangeOptions" |
|
||||
(change)="onChangeDateRange($event.value)" |
|
||||
></gf-toggle> |
|
||||
</div> |
|
||||
<mat-card class="p-0"> |
|
||||
<mat-card-content> |
|
||||
<gf-positions |
|
||||
[baseCurrency]="user?.settings?.baseCurrency" |
|
||||
[deviceType]="deviceType" |
|
||||
[locale]="user?.settings?.locale" |
|
||||
[positions]="positions" |
|
||||
[range]="dateRange" |
|
||||
></gf-positions> |
|
||||
</mat-card-content> |
|
||||
</mat-card> |
|
||||
<div *ngIf="hasPermissionToCreateOrder" class="text-center"> |
|
||||
<a |
|
||||
class="mt-3" |
|
||||
i18n |
|
||||
mat-button |
|
||||
[routerLink]="['/portfolio', 'transactions']" |
|
||||
>Manage Transactions...</a |
|
||||
> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</mat-tab> |
|
||||
<mat-tab> |
|
||||
<ng-template mat-tab-label> |
|
||||
<ion-icon name="reader-outline" size="large"></ion-icon> |
|
||||
</ng-template> |
|
||||
<div class="container pb-3 px-3 positions"> |
|
||||
<div class="row"> |
|
||||
<div class="col-xs-12 col-md-8 offset-md-2"> |
|
||||
<mat-card class="h-100"> |
|
||||
<mat-card-header> |
|
||||
<mat-card-title i18n>Summary</mat-card-title> |
|
||||
</mat-card-header> |
|
||||
<mat-card-content> |
|
||||
<gf-portfolio-summary |
|
||||
[baseCurrency]="user?.settings?.baseCurrency" |
|
||||
[isLoading]="isLoadingSummary" |
|
||||
[locale]="user?.settings?.locale" |
|
||||
[summary]="summary" |
|
||||
></gf-portfolio-summary> |
|
||||
</mat-card-content> |
|
||||
</mat-card> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</mat-tab> |
|
||||
<mat-tab *ngIf="hasPermissionToAccessFearAndGreedIndex"> |
|
||||
<ng-template mat-tab-label> |
|
||||
<ion-icon name="newspaper-outline" size="large"></ion-icon> |
|
||||
</ng-template> |
|
||||
<div |
|
||||
class=" |
|
||||
align-items-center |
|
||||
container |
|
||||
d-flex |
|
||||
flex-grow-1 |
|
||||
h-100 |
|
||||
justify-content-center |
|
||||
w-100 |
|
||||
" |
|
||||
> |
|
||||
<div class="row w-100"> |
|
||||
<div class="col-xs-12 col-md-8 offset-md-2"> |
|
||||
<mat-card class="h-100"> |
|
||||
<mat-card-content> |
|
||||
<gf-fear-and-greed-index |
|
||||
class="d-flex justify-content-center" |
|
||||
[fearAndGreedIndex]="fearAndGreedIndex" |
|
||||
></gf-fear-and-greed-index> |
|
||||
</mat-card-content> |
|
||||
</mat-card> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</mat-tab> |
|
||||
</mat-tab-group> |
|
||||
|
@ -1,94 +1,14 @@ |
|||||
<mat-tab-group |
<router-outlet></router-outlet> |
||||
animationDuration="0ms" |
|
||||
class="position-absolute" |
|
||||
headerPosition="below" |
|
||||
mat-align-tabs="center" |
|
||||
[disablePagination]="true" |
|
||||
(selectedTabChange)="onTabChanged($event)" |
|
||||
> |
|
||||
<mat-tab> |
|
||||
<ng-template mat-tab-label> |
|
||||
<ion-icon name="analytics-outline" size="large"></ion-icon> |
|
||||
</ng-template> |
|
||||
<div |
|
||||
class=" |
|
||||
container |
|
||||
d-flex |
|
||||
flex-column |
|
||||
h-100 |
|
||||
justify-content-center |
|
||||
overview |
|
||||
position-relative |
|
||||
" |
|
||||
> |
|
||||
<div class="row"> |
|
||||
<div |
|
||||
class="chart-container d-flex flex-column col justify-content-center" |
|
||||
> |
|
||||
<gf-line-chart |
|
||||
class="mr-3" |
|
||||
symbol="Performance" |
|
||||
[historicalDataItems]="historicalDataItems" |
|
||||
[showGradient]="true" |
|
||||
[showLoader]="false" |
|
||||
[showXAxis]="false" |
|
||||
[showYAxis]="false" |
|
||||
></gf-line-chart> |
|
||||
<div |
|
||||
*ngIf="historicalDataItems?.length === 0" |
|
||||
class="d-flex justify-content-center" |
|
||||
> |
|
||||
<gf-no-transactions-info-indicator></gf-no-transactions-info-indicator> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
<div class="overview-container row mb-5 mt-1"> |
|
||||
<div class="col"> |
|
||||
<gf-portfolio-performance |
|
||||
class="pb-4" |
|
||||
[baseCurrency]="user?.settings?.baseCurrency" |
|
||||
[isAllTimeHigh]="isAllTimeHigh" |
|
||||
[isAllTimeLow]="isAllTimeLow" |
|
||||
[isLoading]="isLoadingPerformance" |
|
||||
[locale]="user?.settings?.locale" |
|
||||
[performance]="performance" |
|
||||
[showDetails]="!hasImpersonationId && !user.settings.isRestrictedView" |
|
||||
></gf-portfolio-performance> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</mat-tab> |
|
||||
|
|
||||
<mat-tab> |
<nav mat-align-tabs="center" mat-tab-nav-bar> |
||||
<ng-template mat-tab-label> |
<a |
||||
<ion-icon name="wallet-outline" size="large"></ion-icon> |
*ngFor="let tab of tabs" |
||||
</ng-template> |
#rla="routerLinkActive" |
||||
<div class="container justify-content-center pb-3 px-3 positions"> |
mat-tab-link |
||||
<h3 class="d-flex justify-content-center mb-3" i18n>Holdings</h3> |
routerLinkActive |
||||
<div class="row"> |
[active]="rla.isActive" |
||||
<div class="align-items-center col"> |
[routerLink]="tab.path" |
||||
<mat-card class="p-0"> |
> |
||||
<mat-card-content> |
<ion-icon size="large" [name]="tab.iconName"></ion-icon> |
||||
<gf-positions |
</a> |
||||
[baseCurrency]="user?.settings?.baseCurrency" |
</nav> |
||||
[deviceType]="deviceType" |
|
||||
[locale]="user?.settings?.locale" |
|
||||
[positions]="positions" |
|
||||
[range]="dateRange" |
|
||||
></gf-positions> |
|
||||
</mat-card-content> |
|
||||
</mat-card> |
|
||||
<div *ngIf="hasPermissionToCreateOrder" class="text-center"> |
|
||||
<a |
|
||||
class="mt-3" |
|
||||
i18n |
|
||||
mat-button |
|
||||
[routerLink]="['/portfolio', 'transactions']" |
|
||||
>Manage Transactions...</a |
|
||||
> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</mat-tab> |
|
||||
</mat-tab-group> |
|
||||
|
Loading…
Reference in new issue