Feature/add lazy loaded activities table to position detail dialog (#2753)
* Add lazy-loaded activities table * Update changelog
This commit is contained in:
parent
28706d7b26
commit
4547c5da1d
@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
- Introduced the lazy-loaded activities table to the position detail dialog (experimental)
|
||||||
- Improved the font weight in the value component
|
- Improved the font weight in the value component
|
||||||
- Improved the language localization for Türkçe (`tr`)
|
- Improved the language localization for Türkçe (`tr`)
|
||||||
- Upgraded to _Inter_ 4 font family
|
- Upgraded to _Inter_ 4 font family
|
||||||
|
@ -7,12 +7,16 @@ 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 { MatTableDataSource } from '@angular/material/table';
|
||||||
import { DataService } from '@ghostfolio/client/services/data.service';
|
import { DataService } from '@ghostfolio/client/services/data.service';
|
||||||
|
import { UserService } from '@ghostfolio/client/services/user/user.service';
|
||||||
import { DATE_FORMAT, downloadAsFile } from '@ghostfolio/common/helper';
|
import { DATE_FORMAT, downloadAsFile } from '@ghostfolio/common/helper';
|
||||||
import {
|
import {
|
||||||
DataProviderInfo,
|
DataProviderInfo,
|
||||||
EnhancedSymbolProfile,
|
EnhancedSymbolProfile,
|
||||||
LineChartItem
|
LineChartItem,
|
||||||
|
User
|
||||||
} from '@ghostfolio/common/interfaces';
|
} from '@ghostfolio/common/interfaces';
|
||||||
import { OrderWithAccount } from '@ghostfolio/common/types';
|
import { OrderWithAccount } from '@ghostfolio/common/types';
|
||||||
import { translate } from '@ghostfolio/ui/i18n';
|
import { translate } from '@ghostfolio/ui/i18n';
|
||||||
@ -31,6 +35,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 activities: OrderWithAccount[];
|
||||||
public assetClass: string;
|
public assetClass: string;
|
||||||
public assetSubClass: string;
|
public assetSubClass: string;
|
||||||
public averagePrice: number;
|
public averagePrice: number;
|
||||||
@ -39,6 +44,7 @@ export class PositionDetailDialog implements OnDestroy, OnInit {
|
|||||||
[code: string]: { name: string; value: number };
|
[code: string]: { name: string; value: number };
|
||||||
};
|
};
|
||||||
public dataProviderInfo: DataProviderInfo;
|
public dataProviderInfo: DataProviderInfo;
|
||||||
|
public dataSource: MatTableDataSource<OrderWithAccount>;
|
||||||
public dividendInBaseCurrency: number;
|
public dividendInBaseCurrency: number;
|
||||||
public feeInBaseCurrency: number;
|
public feeInBaseCurrency: number;
|
||||||
public firstBuyDate: string;
|
public firstBuyDate: string;
|
||||||
@ -51,16 +57,19 @@ export class PositionDetailDialog implements OnDestroy, OnInit {
|
|||||||
public minPrice: number;
|
public minPrice: number;
|
||||||
public netPerformance: number;
|
public netPerformance: number;
|
||||||
public netPerformancePercent: number;
|
public netPerformancePercent: number;
|
||||||
public orders: OrderWithAccount[];
|
|
||||||
public quantity: number;
|
public quantity: number;
|
||||||
public quantityPrecision = 2;
|
public quantityPrecision = 2;
|
||||||
public reportDataGlitchMail: string;
|
public reportDataGlitchMail: string;
|
||||||
public sectors: {
|
public sectors: {
|
||||||
[name: string]: { name: string; value: number };
|
[name: string]: { name: string; value: number };
|
||||||
};
|
};
|
||||||
|
public sortColumn = 'date';
|
||||||
|
public sortDirection: SortDirection = 'desc';
|
||||||
public SymbolProfile: EnhancedSymbolProfile;
|
public SymbolProfile: EnhancedSymbolProfile;
|
||||||
public tags: Tag[];
|
public tags: Tag[];
|
||||||
|
public totalItems: number;
|
||||||
public transactionCount: number;
|
public transactionCount: number;
|
||||||
|
public user: User;
|
||||||
public value: number;
|
public value: number;
|
||||||
|
|
||||||
private unsubscribeSubject = new Subject<void>();
|
private unsubscribeSubject = new Subject<void>();
|
||||||
@ -69,7 +78,8 @@ export class PositionDetailDialog implements OnDestroy, OnInit {
|
|||||||
private changeDetectorRef: ChangeDetectorRef,
|
private changeDetectorRef: ChangeDetectorRef,
|
||||||
private dataService: DataService,
|
private dataService: DataService,
|
||||||
public dialogRef: MatDialogRef<PositionDetailDialog>,
|
public dialogRef: MatDialogRef<PositionDetailDialog>,
|
||||||
@Inject(MAT_DIALOG_DATA) public data: PositionDetailDialogParams
|
@Inject(MAT_DIALOG_DATA) public data: PositionDetailDialogParams,
|
||||||
|
private userService: UserService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public ngOnInit(): void {
|
public ngOnInit(): void {
|
||||||
@ -102,10 +112,12 @@ export class PositionDetailDialog implements OnDestroy, OnInit {
|
|||||||
transactionCount,
|
transactionCount,
|
||||||
value
|
value
|
||||||
}) => {
|
}) => {
|
||||||
|
this.activities = orders;
|
||||||
this.averagePrice = averagePrice;
|
this.averagePrice = averagePrice;
|
||||||
this.benchmarkDataItems = [];
|
this.benchmarkDataItems = [];
|
||||||
this.countries = {};
|
this.countries = {};
|
||||||
this.dataProviderInfo = dataProviderInfo;
|
this.dataProviderInfo = dataProviderInfo;
|
||||||
|
this.dataSource = new MatTableDataSource(orders.reverse());
|
||||||
this.dividendInBaseCurrency = dividendInBaseCurrency;
|
this.dividendInBaseCurrency = dividendInBaseCurrency;
|
||||||
this.feeInBaseCurrency = feeInBaseCurrency;
|
this.feeInBaseCurrency = feeInBaseCurrency;
|
||||||
this.firstBuyDate = firstBuyDate;
|
this.firstBuyDate = firstBuyDate;
|
||||||
@ -130,7 +142,6 @@ export class PositionDetailDialog implements OnDestroy, OnInit {
|
|||||||
this.minPrice = minPrice;
|
this.minPrice = minPrice;
|
||||||
this.netPerformance = netPerformance;
|
this.netPerformance = netPerformance;
|
||||||
this.netPerformancePercent = netPerformancePercent;
|
this.netPerformancePercent = netPerformancePercent;
|
||||||
this.orders = orders;
|
|
||||||
this.quantity = quantity;
|
this.quantity = quantity;
|
||||||
this.reportDataGlitchMail = `mailto:hi@ghostfol.io?Subject=Ghostfolio Data Glitch Report&body=Hello%0D%0DI would like to report a data glitch for%0D%0DSymbol: ${SymbolProfile?.symbol}%0DData Source: ${SymbolProfile?.dataSource}%0D%0DAdditional notes:%0D%0DCan you please take a look?%0D%0DKind regards`;
|
this.reportDataGlitchMail = `mailto:hi@ghostfol.io?Subject=Ghostfolio Data Glitch Report&body=Hello%0D%0DI would like to report a data glitch for%0D%0DSymbol: ${SymbolProfile?.symbol}%0DData Source: ${SymbolProfile?.dataSource}%0D%0DAdditional notes:%0D%0DCan you please take a look?%0D%0DKind regards`;
|
||||||
this.sectors = {};
|
this.sectors = {};
|
||||||
@ -142,6 +153,7 @@ export class PositionDetailDialog implements OnDestroy, OnInit {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
this.transactionCount = transactionCount;
|
this.transactionCount = transactionCount;
|
||||||
|
this.totalItems = transactionCount;
|
||||||
this.value = value;
|
this.value = value;
|
||||||
|
|
||||||
if (SymbolProfile?.assetClass) {
|
if (SymbolProfile?.assetClass) {
|
||||||
@ -239,6 +251,16 @@ export class PositionDetailDialog implements OnDestroy, OnInit {
|
|||||||
this.changeDetectorRef.markForCheck();
|
this.changeDetectorRef.markForCheck();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.userService.stateChanged
|
||||||
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
|
.subscribe((state) => {
|
||||||
|
if (state?.user) {
|
||||||
|
this.user = state.user;
|
||||||
|
|
||||||
|
this.changeDetectorRef.markForCheck();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public onClose(): void {
|
public onClose(): void {
|
||||||
@ -246,12 +268,20 @@ export class PositionDetailDialog implements OnDestroy, OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public onExport() {
|
public onExport() {
|
||||||
|
let activityIds = [];
|
||||||
|
|
||||||
|
if (this.user?.settings?.isExperimentalFeatures === true) {
|
||||||
|
activityIds = this.dataSource.data.map(({ id }) => {
|
||||||
|
return id;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
activityIds = this.activities.map(({ id }) => {
|
||||||
|
return id;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
this.dataService
|
this.dataService
|
||||||
.fetchExport(
|
.fetchExport(activityIds)
|
||||||
this.orders.map((order) => {
|
|
||||||
return order.id;
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.pipe(takeUntil(this.unsubscribeSubject))
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
.subscribe((data) => {
|
.subscribe((data) => {
|
||||||
downloadAsFile({
|
downloadAsFile({
|
||||||
|
@ -246,11 +246,30 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row" [ngClass]="{ 'd-none': !orders?.length }">
|
<div class="row" [ngClass]="{ 'd-none': !activities?.length }">
|
||||||
<div class="col mb-3">
|
<div class="col mb-3">
|
||||||
<div class="h5 mb-0" i18n>Activities</div>
|
<div class="h5 mb-0" i18n>Activities</div>
|
||||||
|
<gf-activities-table-lazy
|
||||||
|
*ngIf="user?.settings?.isExperimentalFeatures === true"
|
||||||
|
[baseCurrency]="data.baseCurrency"
|
||||||
|
[dataSource]="dataSource"
|
||||||
|
[deviceType]="data.deviceType"
|
||||||
|
[hasPermissionToCreateActivity]="false"
|
||||||
|
[hasPermissionToExportActivities]="!hasImpersonationId && !user.settings.isRestrictedView"
|
||||||
|
[hasPermissionToFilter]="false"
|
||||||
|
[hasPermissionToOpenDetails]="false"
|
||||||
|
[locale]="data.locale"
|
||||||
|
[showActions]="false"
|
||||||
|
[showNameColumn]="false"
|
||||||
|
[sortColumn]="sortColumn"
|
||||||
|
[sortDirection]="sortDirection"
|
||||||
|
[sortDisabled]="true"
|
||||||
|
[totalItems]="totalItems"
|
||||||
|
(export)="onExport()"
|
||||||
|
></gf-activities-table-lazy>
|
||||||
<gf-activities-table
|
<gf-activities-table
|
||||||
[activities]="orders"
|
*ngIf="user?.settings?.isExperimentalFeatures !== true"
|
||||||
|
[activities]="activities"
|
||||||
[baseCurrency]="data.baseCurrency"
|
[baseCurrency]="data.baseCurrency"
|
||||||
[deviceType]="data.deviceType"
|
[deviceType]="data.deviceType"
|
||||||
[hasPermissionToCreateActivity]="false"
|
[hasPermissionToCreateActivity]="false"
|
||||||
@ -277,7 +296,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
*ngIf="data.hasPermissionToReportDataGlitch === true && orders?.length > 0"
|
*ngIf="activities?.length > 0 && data.hasPermissionToReportDataGlitch === true"
|
||||||
class="row"
|
class="row"
|
||||||
>
|
>
|
||||||
<div class="col">
|
<div class="col">
|
||||||
|
@ -5,6 +5,7 @@ import { MatChipsModule } from '@angular/material/chips';
|
|||||||
import { MatDialogModule } from '@angular/material/dialog';
|
import { MatDialogModule } from '@angular/material/dialog';
|
||||||
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 { GfActivitiesTableLazyModule } from '@ghostfolio/ui/activities-table-lazy/activities-table-lazy.module';
|
||||||
import { GfActivitiesTableModule } from '@ghostfolio/ui/activities-table/activities-table.module';
|
import { GfActivitiesTableModule } from '@ghostfolio/ui/activities-table/activities-table.module';
|
||||||
import { GfDataProviderCreditsModule } from '@ghostfolio/ui/data-provider-credits/data-provider-credits.module';
|
import { GfDataProviderCreditsModule } from '@ghostfolio/ui/data-provider-credits/data-provider-credits.module';
|
||||||
import { GfLineChartModule } from '@ghostfolio/ui/line-chart/line-chart.module';
|
import { GfLineChartModule } from '@ghostfolio/ui/line-chart/line-chart.module';
|
||||||
@ -19,6 +20,7 @@ import { PositionDetailDialog } from './position-detail-dialog.component';
|
|||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
GfActivitiesTableModule,
|
GfActivitiesTableModule,
|
||||||
|
GfActivitiesTableLazyModule,
|
||||||
GfDataProviderCreditsModule,
|
GfDataProviderCreditsModule,
|
||||||
GfDialogFooterModule,
|
GfDialogFooterModule,
|
||||||
GfDialogHeaderModule,
|
GfDialogHeaderModule,
|
||||||
|
@ -72,6 +72,7 @@
|
|||||||
[dataSource]="dataSource"
|
[dataSource]="dataSource"
|
||||||
[matSortActive]="sortColumn"
|
[matSortActive]="sortColumn"
|
||||||
[matSortDirection]="sortDirection"
|
[matSortDirection]="sortDirection"
|
||||||
|
[matSortDisabled]="sortDisabled"
|
||||||
>
|
>
|
||||||
<ng-container matColumnDef="select">
|
<ng-container matColumnDef="select">
|
||||||
<th *matHeaderCellDef class="px-1" mat-header-cell>
|
<th *matHeaderCellDef class="px-1" mat-header-cell>
|
||||||
|
@ -48,6 +48,7 @@ export class ActivitiesTableLazyComponent
|
|||||||
@Input() showNameColumn = true;
|
@Input() showNameColumn = true;
|
||||||
@Input() sortColumn: string;
|
@Input() sortColumn: string;
|
||||||
@Input() sortDirection: SortDirection;
|
@Input() sortDirection: SortDirection;
|
||||||
|
@Input() sortDisabled = false;
|
||||||
@Input() totalItems = Number.MAX_SAFE_INTEGER;
|
@Input() totalItems = Number.MAX_SAFE_INTEGER;
|
||||||
|
|
||||||
@Output() activityDeleted = new EventEmitter<string>();
|
@Output() activityDeleted = new EventEmitter<string>();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user