Feature/improve impersonation mode (#293)
* Improve the impersonation mode * Update changelog
This commit is contained in:
parent
308b218487
commit
0ee2258af8
12
CHANGELOG.md
12
CHANGELOG.md
@ -5,6 +5,18 @@ All notable changes to this project will be documented in this file.
|
|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## Unreleased
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fixed an issue with the performance in the portfolio summary tab on the home page (impersonation mode)
|
||||||
|
- Fixed various values in the impersonation mode which have not been nullified
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
|
||||||
|
- Removed the current net performance
|
||||||
|
- Removed the read foreign portfolio permission
|
||||||
|
|
||||||
## 1.38.0 - 14.08.2021
|
## 1.38.0 - 14.08.2021
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
@ -84,25 +84,19 @@ export class AccountController {
|
|||||||
public async getAllAccounts(
|
public async getAllAccounts(
|
||||||
@Headers('impersonation-id') impersonationId
|
@Headers('impersonation-id') impersonationId
|
||||||
): Promise<AccountModel[]> {
|
): Promise<AccountModel[]> {
|
||||||
const impersonationUserId = await this.impersonationService.validateImpersonationId(
|
const impersonationUserId =
|
||||||
impersonationId,
|
await this.impersonationService.validateImpersonationId(
|
||||||
this.request.user.id
|
impersonationId,
|
||||||
|
this.request.user.id
|
||||||
|
);
|
||||||
|
|
||||||
|
let accounts = await this.accountService.getAccounts(
|
||||||
|
impersonationUserId || this.request.user.id
|
||||||
);
|
);
|
||||||
|
|
||||||
let accounts = await this.accountService.accounts({
|
if (impersonationUserId) {
|
||||||
include: { Order: true, Platform: true },
|
|
||||||
orderBy: { name: 'asc' },
|
|
||||||
where: { userId: impersonationUserId || this.request.user.id }
|
|
||||||
});
|
|
||||||
|
|
||||||
if (
|
|
||||||
impersonationUserId &&
|
|
||||||
!hasPermission(
|
|
||||||
getPermissions(this.request.user.role),
|
|
||||||
permissions.readForeignPortfolio
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
accounts = nullifyValuesInObjects(accounts, [
|
accounts = nullifyValuesInObjects(accounts, [
|
||||||
|
'balance',
|
||||||
'fee',
|
'fee',
|
||||||
'quantity',
|
'quantity',
|
||||||
'unitPrice'
|
'unitPrice'
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data.service';
|
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data.service';
|
||||||
import { PrismaService } from '@ghostfolio/api/services/prisma.service';
|
import { PrismaService } from '@ghostfolio/api/services/prisma.service';
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { Account, Currency, Order, Prisma } from '@prisma/client';
|
import { Account, Currency, Order, Platform, Prisma } from '@prisma/client';
|
||||||
|
|
||||||
import { CashDetails } from './interfaces/cash-details.interface';
|
import { CashDetails } from './interfaces/cash-details.interface';
|
||||||
|
|
||||||
@ -41,7 +41,12 @@ export class AccountService {
|
|||||||
cursor?: Prisma.AccountWhereUniqueInput;
|
cursor?: Prisma.AccountWhereUniqueInput;
|
||||||
where?: Prisma.AccountWhereInput;
|
where?: Prisma.AccountWhereInput;
|
||||||
orderBy?: Prisma.AccountOrderByInput;
|
orderBy?: Prisma.AccountOrderByInput;
|
||||||
}): Promise<Account[]> {
|
}): Promise<
|
||||||
|
(Account & {
|
||||||
|
Order?: Order[];
|
||||||
|
Platform?: Platform;
|
||||||
|
})[]
|
||||||
|
> {
|
||||||
const { include, skip, take, cursor, where, orderBy } = params;
|
const { include, skip, take, cursor, where, orderBy } = params;
|
||||||
|
|
||||||
return this.prismaService.account.findMany({
|
return this.prismaService.account.findMany({
|
||||||
@ -72,6 +77,22 @@ export class AccountService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async getAccounts(aUserId: string) {
|
||||||
|
const accounts = await this.accounts({
|
||||||
|
include: { Order: true, Platform: true },
|
||||||
|
orderBy: { name: 'asc' },
|
||||||
|
where: { userId: aUserId }
|
||||||
|
});
|
||||||
|
|
||||||
|
return accounts.map((account) => {
|
||||||
|
const result = { ...account, transactionCount: account.Order.length };
|
||||||
|
|
||||||
|
delete result.Order;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public async getCashDetails(
|
public async getCashDetails(
|
||||||
aUserId: string,
|
aUserId: string,
|
||||||
aCurrency: Currency
|
aCurrency: Currency
|
||||||
|
@ -88,13 +88,7 @@ export class OrderController {
|
|||||||
where: { userId: impersonationUserId || this.request.user.id }
|
where: { userId: impersonationUserId || this.request.user.id }
|
||||||
});
|
});
|
||||||
|
|
||||||
if (
|
if (impersonationUserId) {
|
||||||
impersonationUserId &&
|
|
||||||
!hasPermission(
|
|
||||||
getPermissions(this.request.user.role),
|
|
||||||
permissions.readForeignPortfolio
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
orders = nullifyValuesInObjects(orders, ['fee', 'quantity', 'unitPrice']);
|
orders = nullifyValuesInObjects(orders, ['fee', 'quantity', 'unitPrice']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,11 +11,6 @@ import {
|
|||||||
PortfolioSummary
|
PortfolioSummary
|
||||||
} from '@ghostfolio/common/interfaces';
|
} from '@ghostfolio/common/interfaces';
|
||||||
import { InvestmentItem } from '@ghostfolio/common/interfaces/investment-item.interface';
|
import { InvestmentItem } from '@ghostfolio/common/interfaces/investment-item.interface';
|
||||||
import {
|
|
||||||
getPermissions,
|
|
||||||
hasPermission,
|
|
||||||
permissions
|
|
||||||
} from '@ghostfolio/common/permissions';
|
|
||||||
import { RequestWithUser } from '@ghostfolio/common/types';
|
import { RequestWithUser } from '@ghostfolio/common/types';
|
||||||
import {
|
import {
|
||||||
Controller,
|
Controller,
|
||||||
@ -30,7 +25,6 @@ import {
|
|||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { REQUEST } from '@nestjs/core';
|
import { REQUEST } from '@nestjs/core';
|
||||||
import { AuthGuard } from '@nestjs/passport';
|
import { AuthGuard } from '@nestjs/passport';
|
||||||
import Big from 'big.js';
|
|
||||||
import { Response } from 'express';
|
import { Response } from 'express';
|
||||||
import { StatusCodes, getReasonPhrase } from 'http-status-codes';
|
import { StatusCodes, getReasonPhrase } from 'http-status-codes';
|
||||||
|
|
||||||
@ -50,7 +44,7 @@ export class PortfolioController {
|
|||||||
@Inject(REQUEST) private readonly request: RequestWithUser
|
@Inject(REQUEST) private readonly request: RequestWithUser
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@Get('/investments')
|
@Get('investments')
|
||||||
@UseGuards(AuthGuard('jwt'))
|
@UseGuards(AuthGuard('jwt'))
|
||||||
public async findAll(
|
public async findAll(
|
||||||
@Headers('impersonation-id') impersonationId
|
@Headers('impersonation-id') impersonationId
|
||||||
@ -59,13 +53,7 @@ export class PortfolioController {
|
|||||||
impersonationId
|
impersonationId
|
||||||
);
|
);
|
||||||
|
|
||||||
if (
|
if (impersonationId) {
|
||||||
impersonationId &&
|
|
||||||
!hasPermission(
|
|
||||||
getPermissions(this.request.user.role),
|
|
||||||
permissions.readForeignPortfolio
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
const maxInvestment = investments.reduce(
|
const maxInvestment = investments.reduce(
|
||||||
(investment, item) => Math.max(investment, item.investment),
|
(investment, item) => Math.max(investment, item.investment),
|
||||||
1
|
1
|
||||||
@ -104,13 +92,7 @@ export class PortfolioController {
|
|||||||
res.status(StatusCodes.ACCEPTED);
|
res.status(StatusCodes.ACCEPTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (impersonationId) {
|
||||||
impersonationId &&
|
|
||||||
!hasPermission(
|
|
||||||
getPermissions(this.request.user.role),
|
|
||||||
permissions.readForeignPortfolio
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
let maxValue = 0;
|
let maxValue = 0;
|
||||||
|
|
||||||
chartData.forEach((portfolioItem) => {
|
chartData.forEach((portfolioItem) => {
|
||||||
@ -139,17 +121,8 @@ export class PortfolioController {
|
|||||||
): Promise<{ [symbol: string]: PortfolioPosition }> {
|
): Promise<{ [symbol: string]: PortfolioPosition }> {
|
||||||
let details: { [symbol: string]: PortfolioPosition } = {};
|
let details: { [symbol: string]: PortfolioPosition } = {};
|
||||||
|
|
||||||
const impersonationUserId =
|
|
||||||
await this.impersonationService.validateImpersonationId(
|
|
||||||
impersonationId,
|
|
||||||
this.request.user.id
|
|
||||||
);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
details = await this.portfolioService.getDetails(
|
details = await this.portfolioService.getDetails(impersonationId, range);
|
||||||
impersonationUserId,
|
|
||||||
range
|
|
||||||
);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
|
|
||||||
@ -160,13 +133,7 @@ export class PortfolioController {
|
|||||||
res.status(StatusCodes.ACCEPTED);
|
res.status(StatusCodes.ACCEPTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (impersonationId) {
|
||||||
impersonationId &&
|
|
||||||
!hasPermission(
|
|
||||||
getPermissions(this.request.user.role),
|
|
||||||
permissions.readForeignPortfolio
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
const totalInvestment = Object.values(details)
|
const totalInvestment = Object.values(details)
|
||||||
.map((portfolioPosition) => {
|
.map((portfolioPosition) => {
|
||||||
return portfolioPosition.investment;
|
return portfolioPosition.investment;
|
||||||
@ -220,16 +187,9 @@ export class PortfolioController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let performance = performanceInformation.performance;
|
let performance = performanceInformation.performance;
|
||||||
if (
|
if (impersonationId) {
|
||||||
impersonationId &&
|
|
||||||
!hasPermission(
|
|
||||||
getPermissions(this.request.user.role),
|
|
||||||
permissions.readForeignPortfolio
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
performance = nullifyValuesInObject(performance, [
|
performance = nullifyValuesInObject(performance, [
|
||||||
'currentGrossPerformance',
|
'currentGrossPerformance',
|
||||||
'currentNetPerformance',
|
|
||||||
'currentValue'
|
'currentValue'
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@ -253,6 +213,16 @@ export class PortfolioController {
|
|||||||
res.status(StatusCodes.ACCEPTED);
|
res.status(StatusCodes.ACCEPTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (impersonationId) {
|
||||||
|
result.positions = result.positions.map((position) => {
|
||||||
|
return nullifyValuesInObject(position, [
|
||||||
|
'grossPerformance',
|
||||||
|
'investment',
|
||||||
|
'quantity'
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return <any>res.json(result);
|
return <any>res.json(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -263,20 +233,14 @@ export class PortfolioController {
|
|||||||
): Promise<PortfolioSummary> {
|
): Promise<PortfolioSummary> {
|
||||||
let summary = await this.portfolioService.getSummary(impersonationId);
|
let summary = await this.portfolioService.getSummary(impersonationId);
|
||||||
|
|
||||||
if (
|
if (impersonationId) {
|
||||||
impersonationId &&
|
|
||||||
!hasPermission(
|
|
||||||
getPermissions(this.request.user.role),
|
|
||||||
permissions.readForeignPortfolio
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
summary = nullifyValuesInObject(summary, [
|
summary = nullifyValuesInObject(summary, [
|
||||||
'cash',
|
'cash',
|
||||||
'committedFunds',
|
'committedFunds',
|
||||||
'currentGrossPerformance',
|
'currentGrossPerformance',
|
||||||
'currentNetPerformance',
|
|
||||||
'currentValue',
|
'currentValue',
|
||||||
'fees',
|
'fees',
|
||||||
|
'netWorth',
|
||||||
'totalBuy',
|
'totalBuy',
|
||||||
'totalSell'
|
'totalSell'
|
||||||
]);
|
]);
|
||||||
@ -297,14 +261,12 @@ export class PortfolioController {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (position) {
|
if (position) {
|
||||||
if (
|
if (impersonationId) {
|
||||||
impersonationId &&
|
position = nullifyValuesInObject(position, [
|
||||||
!hasPermission(
|
'grossPerformance',
|
||||||
getPermissions(this.request.user.role),
|
'investment',
|
||||||
permissions.readForeignPortfolio
|
'quantity'
|
||||||
)
|
]);
|
||||||
) {
|
|
||||||
position = nullifyValuesInObject(position, ['grossPerformance']);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return position;
|
return position;
|
||||||
|
@ -530,8 +530,6 @@ export class PortfolioService {
|
|||||||
performance: {
|
performance: {
|
||||||
currentGrossPerformance: 0,
|
currentGrossPerformance: 0,
|
||||||
currentGrossPerformancePercent: 0,
|
currentGrossPerformancePercent: 0,
|
||||||
currentNetPerformance: 0,
|
|
||||||
currentNetPerformancePercent: 0,
|
|
||||||
currentValue: 0
|
currentValue: 0
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -556,9 +554,6 @@ export class PortfolioService {
|
|||||||
performance: {
|
performance: {
|
||||||
currentGrossPerformance,
|
currentGrossPerformance,
|
||||||
currentGrossPerformancePercent,
|
currentGrossPerformancePercent,
|
||||||
// TODO: the next two should include fees
|
|
||||||
currentNetPerformance: currentGrossPerformance,
|
|
||||||
currentNetPerformancePercent: currentGrossPerformancePercent,
|
|
||||||
currentValue: currentValue
|
currentValue: currentValue
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -668,7 +663,7 @@ export class PortfolioService {
|
|||||||
const currency = this.request.user.Settings.currency;
|
const currency = this.request.user.Settings.currency;
|
||||||
const userId = await this.getUserId(aImpersonationId);
|
const userId = await this.getUserId(aImpersonationId);
|
||||||
|
|
||||||
const performanceInformation = await this.getPerformance(userId);
|
const performanceInformation = await this.getPerformance(aImpersonationId);
|
||||||
|
|
||||||
const { balance } = await this.accountService.getCashDetails(
|
const { balance } = await this.accountService.getCashDetails(
|
||||||
userId,
|
userId,
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
[tooltip]="element.Platform?.name"
|
[tooltip]="element.Platform?.name"
|
||||||
[url]="element.Platform?.url"
|
[url]="element.Platform?.url"
|
||||||
></gf-symbol-icon>
|
></gf-symbol-icon>
|
||||||
<span>{{ element.name }}</span>
|
<span>{{ element.name }} </span>
|
||||||
<span
|
<span
|
||||||
*ngIf="element.isDefault"
|
*ngIf="element.isDefault"
|
||||||
class="d-lg-inline-block d-none text-muted"
|
class="d-lg-inline-block d-none text-muted"
|
||||||
@ -45,7 +45,7 @@
|
|||||||
<span class="d-none d-sm-block" i18n>Transactions</span>
|
<span class="d-none d-sm-block" i18n>Transactions</span>
|
||||||
</th>
|
</th>
|
||||||
<td *matCellDef="let element" class="px-1 text-right" mat-cell>
|
<td *matCellDef="let element" class="px-1 text-right" mat-cell>
|
||||||
{{ element.Order?.length }}
|
{{ element.transactionCount }}
|
||||||
</td>
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@
|
|||||||
[colorizeSign]="true"
|
[colorizeSign]="true"
|
||||||
[isCurrency]="true"
|
[isCurrency]="true"
|
||||||
[locale]="locale"
|
[locale]="locale"
|
||||||
[value]="isLoading ? undefined : performance?.currentNetPerformance"
|
[value]="isLoading ? undefined : performance?.currentGrossPerformance"
|
||||||
></gf-value>
|
></gf-value>
|
||||||
</div>
|
</div>
|
||||||
<div class="col">
|
<div class="col">
|
||||||
@ -46,7 +46,7 @@
|
|||||||
[isPercent]="true"
|
[isPercent]="true"
|
||||||
[locale]="locale"
|
[locale]="locale"
|
||||||
[value]="
|
[value]="
|
||||||
isLoading ? undefined : performance?.currentNetPerformancePercent
|
isLoading ? undefined : performance?.currentGrossPerformancePercent
|
||||||
"
|
"
|
||||||
></gf-value>
|
></gf-value>
|
||||||
</div>
|
</div>
|
||||||
|
@ -52,7 +52,7 @@ export class PortfolioPerformanceComponent implements OnChanges, OnInit {
|
|||||||
|
|
||||||
new CountUp(
|
new CountUp(
|
||||||
'value',
|
'value',
|
||||||
this.performance?.currentNetPerformancePercent * 100,
|
this.performance?.currentGrossPerformancePercent * 100,
|
||||||
{
|
{
|
||||||
decimalPlaces: 2,
|
decimalPlaces: 2,
|
||||||
duration: 0.75,
|
duration: 0.75,
|
||||||
|
@ -1,16 +1,21 @@
|
|||||||
<ng-container *ngIf="value || value === 0">
|
<ng-container *ngIf="value || value === 0 || value === null">
|
||||||
<div
|
<div
|
||||||
class="d-flex"
|
class="d-flex"
|
||||||
[ngClass]="position === 'end' ? 'justify-content-end' : ''"
|
[ngClass]="position === 'end' ? 'justify-content-end' : ''"
|
||||||
>
|
>
|
||||||
<ng-container *ngIf="isNumber">
|
<ng-container *ngIf="isNumber || value === null">
|
||||||
<div *ngIf="colorizeSign && value > 0" class="mr-1 text-success">+</div>
|
<div *ngIf="colorizeSign && value > 0" class="mr-1 text-success">+</div>
|
||||||
<div *ngIf="colorizeSign && value < 0" class="mr-1 text-danger">-</div>
|
<div *ngIf="colorizeSign && value < 0" class="mr-1 text-danger">-</div>
|
||||||
<div *ngIf="isPercent" [ngClass]="size === 'medium' ? 'h4 mb-0' : ''">
|
<div *ngIf="isPercent" [ngClass]="size === 'medium' ? 'h4 mb-0' : ''">
|
||||||
{{ formattedValue }}%
|
{{ formattedValue }}%
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="!isPercent" [ngClass]="size === 'medium' ? 'h4 mb-0' : ''">
|
<div *ngIf="!isPercent" [ngClass]="size === 'medium' ? 'h4 mb-0' : ''">
|
||||||
{{ formattedValue }}
|
<ng-container *ngIf="value === null">
|
||||||
|
<span class="text-monospace text-muted">***</span>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container *ngIf="value !== null">
|
||||||
|
{{ formattedValue }}
|
||||||
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
<small *ngIf="currency && size === 'medium'" class="ml-1">
|
<small *ngIf="currency && size === 'medium'" class="ml-1">
|
||||||
{{ currency }}
|
{{ currency }}
|
||||||
@ -38,8 +43,3 @@
|
|||||||
width: '5rem'
|
width: '5rem'
|
||||||
}"
|
}"
|
||||||
></ngx-skeleton-loader>
|
></ngx-skeleton-loader>
|
||||||
|
|
||||||
<div *ngIf="value === null">
|
|
||||||
<span class="text-monospace text-muted text-right">***</span>
|
|
||||||
<span *ngIf="currency" class="ml-1 text-muted">{{ currency }}</span>
|
|
||||||
</div>
|
|
||||||
|
@ -58,7 +58,6 @@ export class HomePageComponent implements OnDestroy, OnInit {
|
|||||||
public fearAndGreedIndex: number;
|
public fearAndGreedIndex: number;
|
||||||
public hasImpersonationId: boolean;
|
public hasImpersonationId: boolean;
|
||||||
public hasPermissionToAccessFearAndGreedIndex: boolean;
|
public hasPermissionToAccessFearAndGreedIndex: boolean;
|
||||||
public hasPermissionToReadForeignPortfolio: boolean;
|
|
||||||
public hasPositions: boolean;
|
public hasPositions: boolean;
|
||||||
public historicalDataItems: LineChartItem[];
|
public historicalDataItems: LineChartItem[];
|
||||||
public isLoadingPerformance = true;
|
public isLoadingPerformance = true;
|
||||||
@ -120,11 +119,6 @@ export class HomePageComponent implements OnDestroy, OnInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.hasPermissionToReadForeignPortfolio = hasPermission(
|
|
||||||
this.user.permissions,
|
|
||||||
permissions.readForeignPortfolio
|
|
||||||
);
|
|
||||||
|
|
||||||
this.changeDetectorRef.markForCheck();
|
this.changeDetectorRef.markForCheck();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -61,7 +61,7 @@
|
|||||||
[isLoading]="isLoadingPerformance"
|
[isLoading]="isLoadingPerformance"
|
||||||
[locale]="user?.settings?.locale"
|
[locale]="user?.settings?.locale"
|
||||||
[performance]="performance"
|
[performance]="performance"
|
||||||
[showDetails]="!hasImpersonationId || hasPermissionToReadForeignPortfolio"
|
[showDetails]="!hasImpersonationId"
|
||||||
></gf-portfolio-performance>
|
></gf-portfolio-performance>
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<gf-toggle
|
<gf-toggle
|
||||||
|
@ -19,7 +19,6 @@ import {
|
|||||||
Position,
|
Position,
|
||||||
User
|
User
|
||||||
} from '@ghostfolio/common/interfaces';
|
} from '@ghostfolio/common/interfaces';
|
||||||
import { hasPermission, permissions } from '@ghostfolio/common/permissions';
|
|
||||||
import { DateRange } from '@ghostfolio/common/types';
|
import { DateRange } from '@ghostfolio/common/types';
|
||||||
import { DeviceDetectorService } from 'ngx-device-detector';
|
import { DeviceDetectorService } from 'ngx-device-detector';
|
||||||
import { Subject } from 'rxjs';
|
import { Subject } from 'rxjs';
|
||||||
@ -37,7 +36,6 @@ export class ZenPageComponent implements AfterViewInit, OnDestroy, OnInit {
|
|||||||
public dateRange: DateRange = 'max';
|
public dateRange: DateRange = 'max';
|
||||||
public deviceType: string;
|
public deviceType: string;
|
||||||
public hasImpersonationId: boolean;
|
public hasImpersonationId: boolean;
|
||||||
public hasPermissionToReadForeignPortfolio: boolean;
|
|
||||||
public hasPositions: boolean;
|
public hasPositions: boolean;
|
||||||
public historicalDataItems: LineChartItem[];
|
public historicalDataItems: LineChartItem[];
|
||||||
public isLoadingPerformance = true;
|
public isLoadingPerformance = true;
|
||||||
@ -65,11 +63,6 @@ export class ZenPageComponent implements AfterViewInit, OnDestroy, OnInit {
|
|||||||
if (state?.user) {
|
if (state?.user) {
|
||||||
this.user = state.user;
|
this.user = state.user;
|
||||||
|
|
||||||
this.hasPermissionToReadForeignPortfolio = hasPermission(
|
|
||||||
this.user.permissions,
|
|
||||||
permissions.readForeignPortfolio
|
|
||||||
);
|
|
||||||
|
|
||||||
this.changeDetectorRef.markForCheck();
|
this.changeDetectorRef.markForCheck();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -49,7 +49,7 @@
|
|||||||
[isLoading]="isLoadingPerformance"
|
[isLoading]="isLoadingPerformance"
|
||||||
[locale]="user?.settings?.locale"
|
[locale]="user?.settings?.locale"
|
||||||
[performance]="performance"
|
[performance]="performance"
|
||||||
[showDetails]="!hasImpersonationId || hasPermissionToReadForeignPortfolio"
|
[showDetails]="!hasImpersonationId"
|
||||||
></gf-portfolio-performance>
|
></gf-portfolio-performance>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
export interface PortfolioPerformance {
|
export interface PortfolioPerformance {
|
||||||
currentGrossPerformance: number;
|
currentGrossPerformance: number;
|
||||||
currentGrossPerformancePercent: number;
|
currentGrossPerformancePercent: number;
|
||||||
currentNetPerformance: number;
|
|
||||||
currentNetPerformancePercent: number;
|
|
||||||
currentValue: number;
|
currentValue: number;
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,6 @@ export const permissions = {
|
|||||||
enableSocialLogin: 'enableSocialLogin',
|
enableSocialLogin: 'enableSocialLogin',
|
||||||
enableStatistics: 'enableStatistics',
|
enableStatistics: 'enableStatistics',
|
||||||
enableSubscription: 'enableSubscription',
|
enableSubscription: 'enableSubscription',
|
||||||
readForeignPortfolio: 'readForeignPortfolio',
|
|
||||||
updateAccount: 'updateAccount',
|
updateAccount: 'updateAccount',
|
||||||
updateAuthDevice: 'updateAuthDevice',
|
updateAuthDevice: 'updateAuthDevice',
|
||||||
updateOrder: 'updateOrder',
|
updateOrder: 'updateOrder',
|
||||||
@ -45,7 +44,6 @@ export function getPermissions(aRole: Role): string[] {
|
|||||||
permissions.deleteAuthDevice,
|
permissions.deleteAuthDevice,
|
||||||
permissions.deleteOrder,
|
permissions.deleteOrder,
|
||||||
permissions.deleteUser,
|
permissions.deleteUser,
|
||||||
permissions.readForeignPortfolio,
|
|
||||||
permissions.updateAccount,
|
permissions.updateAccount,
|
||||||
permissions.updateAuthDevice,
|
permissions.updateAuthDevice,
|
||||||
permissions.updateOrder,
|
permissions.updateOrder,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user