Browse Source

Feature/add accounts tab to position detail dialog (#3012)

* Add accounts tab to position detail dialog

* Update changelog
pull/3013/head^2
Thomas Kaul 11 months ago
committed by GitHub
parent
commit
2518a8fd9d
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 6
      CHANGELOG.md
  2. 3
      apps/api/src/app/portfolio/interfaces/portfolio-position-detail.interface.ts
  3. 10
      apps/api/src/app/portfolio/portfolio.service.ts
  4. 6
      apps/client/src/app/components/accounts-table/accounts-table.component.html
  5. 46
      apps/client/src/app/components/accounts-table/accounts-table.component.ts
  6. 7
      apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.component.ts
  7. 36
      apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.html
  8. 4
      apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.module.ts
  9. 1
      apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.html
  10. 1
      libs/ui/src/lib/activities-table/activities-table.component.ts

6
CHANGELOG.md

@ -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

3
apps/api/src/app/portfolio/interfaces/portfolio-position-detail.interface.ts

@ -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;

10
apps/api/src/app/portfolio/portfolio.service.ts

@ -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,

6
apps/client/src/app/components/accounts-table/accounts-table.component.html

@ -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>

46
apps/client/src/app/components/accounts-table/accounts-table.component.ts

@ -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) {

7
apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.component.ts

@ -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 = [];

36
apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.html

@ -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">

4
apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.module.ts

@ -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]

1
apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.html

@ -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"

1
libs/ui/src/lib/activities-table/activities-table.component.ts

@ -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…
Cancel
Save