Feature/add accounts tab to position detail dialog (#3012)
* Add accounts tab to position detail dialog * Update changelog
This commit is contained in:
parent
572dcf075a
commit
2518a8fd9d
@ -5,6 +5,12 @@ 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
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Added an accounts tab to the position detail dialog
|
||||||
|
|
||||||
## 2.52.0 - 2024-02-16
|
## 2.52.0 - 2024-02-16
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
@ -5,9 +5,10 @@ import {
|
|||||||
} from '@ghostfolio/common/interfaces';
|
} from '@ghostfolio/common/interfaces';
|
||||||
import { OrderWithAccount } from '@ghostfolio/common/types';
|
import { OrderWithAccount } from '@ghostfolio/common/types';
|
||||||
|
|
||||||
import { Tag } from '@prisma/client';
|
import { Account, Tag } from '@prisma/client';
|
||||||
|
|
||||||
export interface PortfolioPositionDetail {
|
export interface PortfolioPositionDetail {
|
||||||
|
accounts: Account[];
|
||||||
averagePrice: number;
|
averagePrice: number;
|
||||||
dataProviderInfo: DataProviderInfo;
|
dataProviderInfo: DataProviderInfo;
|
||||||
dividendInBaseCurrency: number;
|
dividendInBaseCurrency: number;
|
||||||
|
@ -657,6 +657,7 @@ export class PortfolioService {
|
|||||||
if (orders.length <= 0) {
|
if (orders.length <= 0) {
|
||||||
return {
|
return {
|
||||||
tags,
|
tags,
|
||||||
|
accounts: [],
|
||||||
averagePrice: undefined,
|
averagePrice: undefined,
|
||||||
dataProviderInfo: undefined,
|
dataProviderInfo: undefined,
|
||||||
dividendInBaseCurrency: undefined,
|
dividendInBaseCurrency: undefined,
|
||||||
@ -739,6 +740,13 @@ export class PortfolioService {
|
|||||||
transactionCount
|
transactionCount
|
||||||
} = position;
|
} = position;
|
||||||
|
|
||||||
|
const accounts: PortfolioPositionDetail['accounts'] = uniqBy(
|
||||||
|
orders,
|
||||||
|
'Account.id'
|
||||||
|
).map(({ Account }) => {
|
||||||
|
return Account;
|
||||||
|
});
|
||||||
|
|
||||||
const dividendInBaseCurrency = getSum(
|
const dividendInBaseCurrency = getSum(
|
||||||
orders
|
orders
|
||||||
.filter(({ type }) => {
|
.filter(({ type }) => {
|
||||||
@ -812,6 +820,7 @@ export class PortfolioService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
accounts,
|
||||||
firstBuyDate,
|
firstBuyDate,
|
||||||
marketPrice,
|
marketPrice,
|
||||||
maxPrice,
|
maxPrice,
|
||||||
@ -894,6 +903,7 @@ export class PortfolioService {
|
|||||||
orders,
|
orders,
|
||||||
SymbolProfile,
|
SymbolProfile,
|
||||||
tags,
|
tags,
|
||||||
|
accounts: [],
|
||||||
averagePrice: 0,
|
averagePrice: 0,
|
||||||
dataProviderInfo: undefined,
|
dataProviderInfo: undefined,
|
||||||
dividendInBaseCurrency: 0,
|
dividendInBaseCurrency: 0,
|
||||||
|
@ -277,14 +277,16 @@
|
|||||||
<tr *matHeaderRowDef="displayedColumns" mat-header-row></tr>
|
<tr *matHeaderRowDef="displayedColumns" mat-header-row></tr>
|
||||||
<tr
|
<tr
|
||||||
*matRowDef="let row; columns: displayedColumns"
|
*matRowDef="let row; columns: displayedColumns"
|
||||||
class="cursor-pointer"
|
|
||||||
mat-row
|
mat-row
|
||||||
|
[ngClass]="{
|
||||||
|
'cursor-pointer': hasPermissionToOpenDetails
|
||||||
|
}"
|
||||||
(click)="onOpenAccountDetailDialog(row.id)"
|
(click)="onOpenAccountDetailDialog(row.id)"
|
||||||
></tr>
|
></tr>
|
||||||
<tr
|
<tr
|
||||||
*matFooterRowDef="displayedColumns"
|
*matFooterRowDef="displayedColumns"
|
||||||
mat-footer-row
|
mat-footer-row
|
||||||
[ngClass]="{ 'd-none': isLoading }"
|
[ngClass]="{ 'd-none': isLoading || !showFooter }"
|
||||||
></tr>
|
></tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
@ -26,8 +26,14 @@ export class AccountsTableComponent implements OnChanges, OnDestroy, OnInit {
|
|||||||
@Input() accounts: AccountModel[];
|
@Input() accounts: AccountModel[];
|
||||||
@Input() baseCurrency: string;
|
@Input() baseCurrency: string;
|
||||||
@Input() deviceType: string;
|
@Input() deviceType: string;
|
||||||
|
@Input() hasPermissionToOpenDetails = true;
|
||||||
@Input() locale: string;
|
@Input() locale: string;
|
||||||
@Input() showActions: boolean;
|
@Input() showActions: boolean;
|
||||||
|
@Input() showBalance = true;
|
||||||
|
@Input() showFooter = true;
|
||||||
|
@Input() showTransactions = true;
|
||||||
|
@Input() showValue = true;
|
||||||
|
@Input() showValueInBaseCurrency = true;
|
||||||
@Input() totalBalanceInBaseCurrency: number;
|
@Input() totalBalanceInBaseCurrency: number;
|
||||||
@Input() totalValueInBaseCurrency: number;
|
@Input() totalValueInBaseCurrency: number;
|
||||||
@Input() transactionCount: number;
|
@Input() transactionCount: number;
|
||||||
@ -51,17 +57,27 @@ export class AccountsTableComponent implements OnChanges, OnDestroy, OnInit {
|
|||||||
public ngOnInit() {}
|
public ngOnInit() {}
|
||||||
|
|
||||||
public ngOnChanges() {
|
public ngOnChanges() {
|
||||||
this.displayedColumns = [
|
this.displayedColumns = ['status', 'account', 'platform'];
|
||||||
'status',
|
|
||||||
'account',
|
if (this.showTransactions) {
|
||||||
'platform',
|
this.displayedColumns.push('transactions');
|
||||||
'transactions',
|
}
|
||||||
'balance',
|
|
||||||
'value',
|
if (this.showBalance) {
|
||||||
'currency',
|
this.displayedColumns.push('balance');
|
||||||
'valueInBaseCurrency',
|
}
|
||||||
'comment'
|
|
||||||
];
|
if (this.showValue) {
|
||||||
|
this.displayedColumns.push('value');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.displayedColumns.push('currency');
|
||||||
|
|
||||||
|
if (this.showValueInBaseCurrency) {
|
||||||
|
this.displayedColumns.push('valueInBaseCurrency');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.displayedColumns.push('comment');
|
||||||
|
|
||||||
if (this.showActions) {
|
if (this.showActions) {
|
||||||
this.displayedColumns.push('actions');
|
this.displayedColumns.push('actions');
|
||||||
@ -89,9 +105,11 @@ export class AccountsTableComponent implements OnChanges, OnDestroy, OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public onOpenAccountDetailDialog(accountId: string) {
|
public onOpenAccountDetailDialog(accountId: string) {
|
||||||
this.router.navigate([], {
|
if (this.hasPermissionToOpenDetails) {
|
||||||
queryParams: { accountId, accountDetailDialog: true }
|
this.router.navigate([], {
|
||||||
});
|
queryParams: { accountId, accountDetailDialog: true }
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public onOpenComment(aComment: string) {
|
public onOpenComment(aComment: string) {
|
||||||
|
@ -19,9 +19,9 @@ import {
|
|||||||
OnInit
|
OnInit
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||||
import { Sort, SortDirection } from '@angular/material/sort';
|
import { SortDirection } from '@angular/material/sort';
|
||||||
import { MatTableDataSource } from '@angular/material/table';
|
import { MatTableDataSource } from '@angular/material/table';
|
||||||
import { Tag } from '@prisma/client';
|
import { Account, Tag } from '@prisma/client';
|
||||||
import { format, isSameMonth, isToday, parseISO } from 'date-fns';
|
import { format, isSameMonth, isToday, parseISO } from 'date-fns';
|
||||||
import { Subject } from 'rxjs';
|
import { Subject } from 'rxjs';
|
||||||
import { takeUntil } from 'rxjs/operators';
|
import { takeUntil } from 'rxjs/operators';
|
||||||
@ -36,6 +36,7 @@ import { PositionDetailDialogParams } from './interfaces/interfaces';
|
|||||||
styleUrls: ['./position-detail-dialog.component.scss']
|
styleUrls: ['./position-detail-dialog.component.scss']
|
||||||
})
|
})
|
||||||
export class PositionDetailDialog implements OnDestroy, OnInit {
|
export class PositionDetailDialog implements OnDestroy, OnInit {
|
||||||
|
public accounts: Account[];
|
||||||
public activities: OrderWithAccount[];
|
public activities: OrderWithAccount[];
|
||||||
public assetClass: string;
|
public assetClass: string;
|
||||||
public assetSubClass: string;
|
public assetSubClass: string;
|
||||||
@ -92,6 +93,7 @@ export class PositionDetailDialog implements OnDestroy, OnInit {
|
|||||||
.pipe(takeUntil(this.unsubscribeSubject))
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
.subscribe(
|
.subscribe(
|
||||||
({
|
({
|
||||||
|
accounts,
|
||||||
averagePrice,
|
averagePrice,
|
||||||
dataProviderInfo,
|
dataProviderInfo,
|
||||||
dividendInBaseCurrency,
|
dividendInBaseCurrency,
|
||||||
@ -113,6 +115,7 @@ export class PositionDetailDialog implements OnDestroy, OnInit {
|
|||||||
transactionCount,
|
transactionCount,
|
||||||
value
|
value
|
||||||
}) => {
|
}) => {
|
||||||
|
this.accounts = accounts;
|
||||||
this.activities = orders;
|
this.activities = orders;
|
||||||
this.averagePrice = averagePrice;
|
this.averagePrice = averagePrice;
|
||||||
this.benchmarkDataItems = [];
|
this.benchmarkDataItems = [];
|
||||||
|
@ -241,9 +241,17 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row" [ngClass]="{ 'd-none': !activities?.length }">
|
<mat-tab-group
|
||||||
<div class="col mb-3">
|
animationDuration="0"
|
||||||
<div class="h5 mb-0" i18n>Activities</div>
|
class="mb-3"
|
||||||
|
[mat-stretch-tabs]="false"
|
||||||
|
[ngClass]="{ 'd-none': !activities?.length }"
|
||||||
|
>
|
||||||
|
<mat-tab>
|
||||||
|
<ng-template mat-tab-label>
|
||||||
|
<ion-icon name="swap-vertical-outline" />
|
||||||
|
<div class="d-none d-sm-block ml-2" i18n>Activities</div>
|
||||||
|
</ng-template>
|
||||||
<gf-activities-table
|
<gf-activities-table
|
||||||
[baseCurrency]="data.baseCurrency"
|
[baseCurrency]="data.baseCurrency"
|
||||||
[dataSource]="dataSource"
|
[dataSource]="dataSource"
|
||||||
@ -261,8 +269,26 @@
|
|||||||
[totalItems]="totalItems"
|
[totalItems]="totalItems"
|
||||||
(export)="onExport()"
|
(export)="onExport()"
|
||||||
/>
|
/>
|
||||||
</div>
|
</mat-tab>
|
||||||
</div>
|
<mat-tab>
|
||||||
|
<ng-template mat-tab-label>
|
||||||
|
<ion-icon name="wallet-outline" />
|
||||||
|
<div class="d-none d-sm-block ml-2" i18n>Accounts</div>
|
||||||
|
</ng-template>
|
||||||
|
<gf-accounts-table
|
||||||
|
[accounts]="accounts"
|
||||||
|
[baseCurrency]="user?.settings?.baseCurrency"
|
||||||
|
[deviceType]="data.deviceType"
|
||||||
|
[hasPermissionToOpenDetails]="false"
|
||||||
|
[locale]="user?.settings?.locale"
|
||||||
|
[showBalance]="false"
|
||||||
|
[showFooter]="false"
|
||||||
|
[showTransactions]="false"
|
||||||
|
[showValue]="false"
|
||||||
|
[showValueInBaseCurrency]="false"
|
||||||
|
/>
|
||||||
|
</mat-tab>
|
||||||
|
</mat-tab-group>
|
||||||
|
|
||||||
<div *ngIf="tags?.length > 0" class="row">
|
<div *ngIf="tags?.length > 0" class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { GfAccountsTableModule } from '@ghostfolio/client/components/accounts-table/accounts-table.module';
|
||||||
import { GfDialogFooterModule } from '@ghostfolio/client/components/dialog-footer/dialog-footer.module';
|
import { GfDialogFooterModule } from '@ghostfolio/client/components/dialog-footer/dialog-footer.module';
|
||||||
import { GfDialogHeaderModule } from '@ghostfolio/client/components/dialog-header/dialog-header.module';
|
import { GfDialogHeaderModule } from '@ghostfolio/client/components/dialog-header/dialog-header.module';
|
||||||
import { GfActivitiesTableModule } from '@ghostfolio/ui/activities-table/activities-table.module';
|
import { GfActivitiesTableModule } from '@ghostfolio/ui/activities-table/activities-table.module';
|
||||||
@ -11,6 +12,7 @@ import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
|
|||||||
import { MatButtonModule } from '@angular/material/button';
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
import { MatChipsModule } from '@angular/material/chips';
|
import { MatChipsModule } from '@angular/material/chips';
|
||||||
import { MatDialogModule } from '@angular/material/dialog';
|
import { MatDialogModule } from '@angular/material/dialog';
|
||||||
|
import { MatTabsModule } from '@angular/material/tabs';
|
||||||
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
|
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
|
||||||
|
|
||||||
import { PositionDetailDialog } from './position-detail-dialog.component';
|
import { PositionDetailDialog } from './position-detail-dialog.component';
|
||||||
@ -19,6 +21,7 @@ import { PositionDetailDialog } from './position-detail-dialog.component';
|
|||||||
declarations: [PositionDetailDialog],
|
declarations: [PositionDetailDialog],
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
|
GfAccountsTableModule,
|
||||||
GfActivitiesTableModule,
|
GfActivitiesTableModule,
|
||||||
GfDataProviderCreditsModule,
|
GfDataProviderCreditsModule,
|
||||||
GfDialogFooterModule,
|
GfDialogFooterModule,
|
||||||
@ -29,6 +32,7 @@ import { PositionDetailDialog } from './position-detail-dialog.component';
|
|||||||
MatButtonModule,
|
MatButtonModule,
|
||||||
MatChipsModule,
|
MatChipsModule,
|
||||||
MatDialogModule,
|
MatDialogModule,
|
||||||
|
MatTabsModule,
|
||||||
NgxSkeletonLoaderModule
|
NgxSkeletonLoaderModule
|
||||||
],
|
],
|
||||||
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||||
|
@ -128,7 +128,6 @@
|
|||||||
[pageSize]="maxSafeInteger"
|
[pageSize]="maxSafeInteger"
|
||||||
[showActions]="false"
|
[showActions]="false"
|
||||||
[showCheckbox]="true"
|
[showCheckbox]="true"
|
||||||
[showFooter]="false"
|
|
||||||
[showSymbolColumn]="false"
|
[showSymbolColumn]="false"
|
||||||
[sortColumn]="sortColumn"
|
[sortColumn]="sortColumn"
|
||||||
[sortDirection]="sortDirection"
|
[sortDirection]="sortDirection"
|
||||||
|
@ -45,7 +45,6 @@ export class ActivitiesTableComponent
|
|||||||
@Input() pageSize = DEFAULT_PAGE_SIZE;
|
@Input() pageSize = DEFAULT_PAGE_SIZE;
|
||||||
@Input() showActions = true;
|
@Input() showActions = true;
|
||||||
@Input() showCheckbox = false;
|
@Input() showCheckbox = false;
|
||||||
@Input() showFooter = true;
|
|
||||||
@Input() showNameColumn = true;
|
@Input() showNameColumn = true;
|
||||||
@Input() sortColumn: string;
|
@Input() sortColumn: string;
|
||||||
@Input() sortDirection: SortDirection;
|
@Input() sortDirection: SortDirection;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user