Add cash balances table to account detail dialog (#2549)
* Add cash balances table to account detail dialog * Update changelog --------- Co-authored-by: Thomas <4159106+dtslvr@users.noreply.github.com>
This commit is contained in:
parent
6f4fd0826c
commit
ed4dd79c72
@ -9,6 +9,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
- Added a historical cash balances table to the account detail dialog
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
- Respected the `withExcludedAccounts` flag in the account balance time series
|
- Respected the `withExcludedAccounts` flag in the account balance time series
|
||||||
|
|
||||||
## 2.27.1 - 2023-11-28
|
## 2.27.1 - 2023-11-28
|
||||||
|
@ -29,14 +29,15 @@ import { AccountDetailDialogParams } from './interfaces/interfaces';
|
|||||||
styleUrls: ['./account-detail-dialog.component.scss']
|
styleUrls: ['./account-detail-dialog.component.scss']
|
||||||
})
|
})
|
||||||
export class AccountDetailDialog implements OnDestroy, OnInit {
|
export class AccountDetailDialog implements OnDestroy, OnInit {
|
||||||
|
public activities: OrderWithAccount[];
|
||||||
public balance: number;
|
public balance: number;
|
||||||
public currency: string;
|
public currency: string;
|
||||||
public equity: number;
|
public equity: number;
|
||||||
public hasImpersonationId: boolean;
|
public hasImpersonationId: boolean;
|
||||||
public historicalDataItems: HistoricalDataItem[];
|
public historicalDataItems: HistoricalDataItem[];
|
||||||
|
public isLoadingActivities: boolean;
|
||||||
public isLoadingChart: boolean;
|
public isLoadingChart: boolean;
|
||||||
public name: string;
|
public name: string;
|
||||||
public orders: OrderWithAccount[];
|
|
||||||
public platformName: string;
|
public platformName: string;
|
||||||
public transactionCount: number;
|
public transactionCount: number;
|
||||||
public user: User;
|
public user: User;
|
||||||
@ -64,6 +65,7 @@ export class AccountDetailDialog implements OnDestroy, OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ngOnInit() {
|
public ngOnInit() {
|
||||||
|
this.isLoadingActivities = true;
|
||||||
this.isLoadingChart = true;
|
this.isLoadingChart = true;
|
||||||
|
|
||||||
this.dataService
|
this.dataService
|
||||||
@ -103,7 +105,9 @@ export class AccountDetailDialog implements OnDestroy, OnInit {
|
|||||||
})
|
})
|
||||||
.pipe(takeUntil(this.unsubscribeSubject))
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
.subscribe(({ activities }) => {
|
.subscribe(({ activities }) => {
|
||||||
this.orders = activities;
|
this.activities = activities;
|
||||||
|
|
||||||
|
this.isLoadingActivities = false;
|
||||||
|
|
||||||
this.changeDetectorRef.markForCheck();
|
this.changeDetectorRef.markForCheck();
|
||||||
});
|
});
|
||||||
@ -153,8 +157,8 @@ export class AccountDetailDialog implements OnDestroy, OnInit {
|
|||||||
public onExport() {
|
public onExport() {
|
||||||
this.dataService
|
this.dataService
|
||||||
.fetchExport(
|
.fetchExport(
|
||||||
this.orders.map((order) => {
|
this.activities.map(({ id }) => {
|
||||||
return order.id;
|
return id;
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
.pipe(takeUntil(this.unsubscribeSubject))
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
></gf-investment-chart>
|
></gf-investment-chart>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="mb-3 row">
|
||||||
<div class="col-6 mb-3">
|
<div class="col-6 mb-3">
|
||||||
<gf-value
|
<gf-value
|
||||||
i18n
|
i18n
|
||||||
@ -64,11 +64,15 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row" [ngClass]="{ 'd-none': !orders?.length }">
|
<mat-tab-group
|
||||||
<div class="col mb-3">
|
animationDuration="0"
|
||||||
<div class="h5 mb-0" i18n>Activities</div>
|
[mat-stretch-tabs]="false"
|
||||||
|
[ngClass]="{ 'd-none': isLoadingActivities }"
|
||||||
|
>
|
||||||
|
<mat-tab>
|
||||||
|
<ng-template i18n mat-tab-label>Activities</ng-template>
|
||||||
<gf-activities-table
|
<gf-activities-table
|
||||||
[activities]="orders"
|
[activities]="activities"
|
||||||
[baseCurrency]="user?.settings?.baseCurrency"
|
[baseCurrency]="user?.settings?.baseCurrency"
|
||||||
[deviceType]="data.deviceType"
|
[deviceType]="data.deviceType"
|
||||||
[hasPermissionToCreateActivity]="false"
|
[hasPermissionToCreateActivity]="false"
|
||||||
@ -79,8 +83,15 @@
|
|||||||
[showActions]="false"
|
[showActions]="false"
|
||||||
(export)="onExport()"
|
(export)="onExport()"
|
||||||
></gf-activities-table>
|
></gf-activities-table>
|
||||||
</div>
|
</mat-tab>
|
||||||
</div>
|
<mat-tab>
|
||||||
|
<ng-template i18n mat-tab-label>Cash Balances</ng-template>
|
||||||
|
<gf-account-balances
|
||||||
|
[accountId]="data.accountId"
|
||||||
|
[locale]="user?.settings?.locale"
|
||||||
|
></gf-account-balances>
|
||||||
|
</mat-tab>
|
||||||
|
</mat-tab-group>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -2,9 +2,11 @@ import { CommonModule } from '@angular/common';
|
|||||||
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
|
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
|
||||||
import { MatButtonModule } from '@angular/material/button';
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
import { MatDialogModule } from '@angular/material/dialog';
|
import { MatDialogModule } from '@angular/material/dialog';
|
||||||
|
import { MatTabsModule } from '@angular/material/tabs';
|
||||||
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 { GfInvestmentChartModule } from '@ghostfolio/client/components/investment-chart/investment-chart.module';
|
import { GfInvestmentChartModule } from '@ghostfolio/client/components/investment-chart/investment-chart.module';
|
||||||
|
import { GfAccountBalancesModule } from '@ghostfolio/ui/account-balances/account-balances.module';
|
||||||
import { GfActivitiesTableModule } from '@ghostfolio/ui/activities-table/activities-table.module';
|
import { GfActivitiesTableModule } from '@ghostfolio/ui/activities-table/activities-table.module';
|
||||||
import { GfValueModule } from '@ghostfolio/ui/value';
|
import { GfValueModule } from '@ghostfolio/ui/value';
|
||||||
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
|
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
|
||||||
@ -15,6 +17,7 @@ import { AccountDetailDialog } from './account-detail-dialog.component';
|
|||||||
declarations: [AccountDetailDialog],
|
declarations: [AccountDetailDialog],
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
|
GfAccountBalancesModule,
|
||||||
GfActivitiesTableModule,
|
GfActivitiesTableModule,
|
||||||
GfDialogFooterModule,
|
GfDialogFooterModule,
|
||||||
GfDialogHeaderModule,
|
GfDialogHeaderModule,
|
||||||
@ -22,6 +25,7 @@ import { AccountDetailDialog } from './account-detail-dialog.component';
|
|||||||
GfValueModule,
|
GfValueModule,
|
||||||
MatButtonModule,
|
MatButtonModule,
|
||||||
MatDialogModule,
|
MatDialogModule,
|
||||||
|
MatTabsModule,
|
||||||
NgxSkeletonLoaderModule
|
NgxSkeletonLoaderModule
|
||||||
],
|
],
|
||||||
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row mb-3">
|
<div class="mb-3 row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<h1 class="d-none d-sm-block h3 mb-3 text-center" i18n>Activities</h1>
|
<h1 class="d-none d-sm-block h3 mb-3 text-center" i18n>Activities</h1>
|
||||||
<gf-activities-table
|
<gf-activities-table
|
||||||
|
@ -18,6 +18,7 @@ import { PropertyDto } from '@ghostfolio/api/services/property/property.dto';
|
|||||||
import { DATE_FORMAT } from '@ghostfolio/common/helper';
|
import { DATE_FORMAT } from '@ghostfolio/common/helper';
|
||||||
import {
|
import {
|
||||||
Access,
|
Access,
|
||||||
|
AccountBalancesResponse,
|
||||||
Accounts,
|
Accounts,
|
||||||
BenchmarkMarketDataDetails,
|
BenchmarkMarketDataDetails,
|
||||||
BenchmarkResponse,
|
BenchmarkResponse,
|
||||||
@ -137,6 +138,12 @@ export class DataService {
|
|||||||
return this.http.get<AccountWithValue>(`/api/v1/account/${aAccountId}`);
|
return this.http.get<AccountWithValue>(`/api/v1/account/${aAccountId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public fetchAccountBalances(aAccountId: string) {
|
||||||
|
return this.http.get<AccountBalancesResponse>(
|
||||||
|
`/api/v1/account/${aAccountId}/balances`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public fetchAccounts() {
|
public fetchAccounts() {
|
||||||
return this.http.get<Accounts>('/api/v1/account');
|
return this.http.get<Accounts>('/api/v1/account');
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,36 @@
|
|||||||
|
<table
|
||||||
|
class="gf-table w-100"
|
||||||
|
mat-table
|
||||||
|
matSort
|
||||||
|
matSortActive="date"
|
||||||
|
matSortDirection="desc"
|
||||||
|
[dataSource]="dataSource"
|
||||||
|
>
|
||||||
|
<ng-container matColumnDef="date">
|
||||||
|
<th *matHeaderCellDef class="px-2" mat-header-cell mat-sort-header>
|
||||||
|
<ng-container i18n>Date</ng-container>
|
||||||
|
</th>
|
||||||
|
<td *matCellDef="let element" class="px-2" mat-cell>
|
||||||
|
<gf-value [isDate]="true" [locale]="locale" [value]="element?.date" />
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="value">
|
||||||
|
<th *matHeaderCellDef class="px-2 text-right" mat-header-cell>
|
||||||
|
<ng-container i18n>Value</ng-container>
|
||||||
|
</th>
|
||||||
|
<td *matCellDef="let element" class="px-2" mat-cell>
|
||||||
|
<div class="d-flex justify-content-end">
|
||||||
|
<gf-value
|
||||||
|
[isCurrency]="true"
|
||||||
|
[locale]="locale"
|
||||||
|
[unit]="element?.Account?.currency"
|
||||||
|
[value]="element?.value"
|
||||||
|
></gf-value>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<tr *matHeaderRowDef="displayedColumns" mat-header-row></tr>
|
||||||
|
<tr *matRowDef="let row; columns: displayedColumns" mat-row></tr>
|
||||||
|
</table>
|
@ -0,0 +1,5 @@
|
|||||||
|
@import 'apps/client/src/styles/ghostfolio-style';
|
||||||
|
|
||||||
|
:host {
|
||||||
|
display: block;
|
||||||
|
}
|
@ -0,0 +1,63 @@
|
|||||||
|
import {
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
ChangeDetectorRef,
|
||||||
|
Component,
|
||||||
|
Input,
|
||||||
|
OnDestroy,
|
||||||
|
OnInit,
|
||||||
|
ViewChild
|
||||||
|
} from '@angular/core';
|
||||||
|
import { MatSort } from '@angular/material/sort';
|
||||||
|
import { MatTableDataSource } from '@angular/material/table';
|
||||||
|
import { DataService } from '@ghostfolio/client/services/data.service';
|
||||||
|
import { AccountBalancesResponse } from '@ghostfolio/common/interfaces';
|
||||||
|
import { get } from 'lodash';
|
||||||
|
import { Subject, takeUntil } from 'rxjs';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
selector: 'gf-account-balances',
|
||||||
|
styleUrls: ['./account-balances.component.scss'],
|
||||||
|
templateUrl: './account-balances.component.html'
|
||||||
|
})
|
||||||
|
export class AccountBalancesComponent implements OnDestroy, OnInit {
|
||||||
|
@Input() accountId: string;
|
||||||
|
@Input() locale: string;
|
||||||
|
|
||||||
|
@ViewChild(MatSort) sort: MatSort;
|
||||||
|
|
||||||
|
public dataSource: MatTableDataSource<
|
||||||
|
AccountBalancesResponse['balances'][0]
|
||||||
|
> = new MatTableDataSource();
|
||||||
|
public displayedColumns: string[] = ['date', 'value'];
|
||||||
|
|
||||||
|
private unsubscribeSubject = new Subject<void>();
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
private changeDetectorRef: ChangeDetectorRef,
|
||||||
|
private dataService: DataService
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public ngOnInit() {
|
||||||
|
this.fetchBalances();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ngOnDestroy() {
|
||||||
|
this.unsubscribeSubject.next();
|
||||||
|
this.unsubscribeSubject.complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
private fetchBalances() {
|
||||||
|
this.dataService
|
||||||
|
.fetchAccountBalances(this.accountId)
|
||||||
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
|
.subscribe(({ balances }) => {
|
||||||
|
this.dataSource = new MatTableDataSource(balances);
|
||||||
|
|
||||||
|
this.dataSource.sort = this.sort;
|
||||||
|
this.dataSource.sortingDataAccessor = get;
|
||||||
|
|
||||||
|
this.changeDetectorRef.markForCheck();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
15
libs/ui/src/lib/account-balances/account-balances.module.ts
Normal file
15
libs/ui/src/lib/account-balances/account-balances.module.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
|
||||||
|
import { MatSortModule } from '@angular/material/sort';
|
||||||
|
import { MatTableModule } from '@angular/material/table';
|
||||||
|
import { GfValueModule } from '@ghostfolio/ui/value';
|
||||||
|
|
||||||
|
import { AccountBalancesComponent } from './account-balances.component';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [AccountBalancesComponent],
|
||||||
|
exports: [AccountBalancesComponent],
|
||||||
|
imports: [CommonModule, GfValueModule, MatSortModule, MatTableModule],
|
||||||
|
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||||
|
})
|
||||||
|
export class GfAccountBalancesModule {}
|
Loading…
x
Reference in New Issue
Block a user