From f3d961bc16bb43356d398ac360059690e4c6dd45 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Sat, 4 May 2024 14:11:37 +0200 Subject: [PATCH] Feature/move holdings table to holdings tab of home page (#3368) * Move holdings table to holdings tab of home page * Deprecate api/v1/portfolio/positions endpoint * Update changelog --- CHANGELOG.md | 1 + .../src/app/portfolio/portfolio.controller.ts | 3 + .../home-holdings/home-holdings.component.ts | 100 +++++----- .../home-holdings/home-holdings.html | 55 +++--- .../home-holdings/home-holdings.module.ts | 8 +- .../interfaces/interfaces.ts | 0 .../position-detail-dialog.component.scss | 0 .../position-detail-dialog.component.ts | 0 .../position-detail-dialog.html | 0 .../position-detail-dialog.module.ts | 0 .../position/position.component.html | 72 -------- .../position/position.component.scss | 13 -- .../components/position/position.component.ts | 40 ---- .../components/position/position.module.ts | 29 --- .../positions/positions.component.html | 35 ---- .../positions/positions.component.scss | 17 -- .../positions/positions.component.ts | 70 ------- .../components/positions/positions.module.ts | 21 --- .../src/app/components/rules/rules.module.ts | 2 - .../pages/home/home-page-routing.module.ts | 5 + .../activities/activities-page.component.ts | 4 +- .../allocations/allocations-page.component.ts | 6 +- .../analysis/analysis-page.component.ts | 6 +- .../holdings/holdings-page-routing.module.ts | 21 --- .../holdings/holdings-page.component.ts | 171 ------------------ .../portfolio/holdings/holdings-page.html | 38 ---- .../holdings/holdings-page.module.ts | 22 --- .../portfolio/holdings/holdings-page.scss | 3 - .../portfolio-page-routing.module.ts | 7 - .../portfolio/portfolio-page.component.ts | 5 - apps/client/src/app/services/data.service.ts | 3 + .../holdings-table.component.ts | 2 +- 32 files changed, 109 insertions(+), 650 deletions(-) rename apps/client/src/app/components/{position => }/position-detail-dialog/interfaces/interfaces.ts (100%) rename apps/client/src/app/components/{position => }/position-detail-dialog/position-detail-dialog.component.scss (100%) rename apps/client/src/app/components/{position => }/position-detail-dialog/position-detail-dialog.component.ts (100%) rename apps/client/src/app/components/{position => }/position-detail-dialog/position-detail-dialog.html (100%) rename apps/client/src/app/components/{position => }/position-detail-dialog/position-detail-dialog.module.ts (100%) delete mode 100644 apps/client/src/app/components/position/position.component.html delete mode 100644 apps/client/src/app/components/position/position.component.scss delete mode 100644 apps/client/src/app/components/position/position.component.ts delete mode 100644 apps/client/src/app/components/position/position.module.ts delete mode 100644 apps/client/src/app/components/positions/positions.component.html delete mode 100644 apps/client/src/app/components/positions/positions.component.scss delete mode 100644 apps/client/src/app/components/positions/positions.component.ts delete mode 100644 apps/client/src/app/components/positions/positions.module.ts delete mode 100644 apps/client/src/app/pages/portfolio/holdings/holdings-page-routing.module.ts delete mode 100644 apps/client/src/app/pages/portfolio/holdings/holdings-page.component.ts delete mode 100644 apps/client/src/app/pages/portfolio/holdings/holdings-page.html delete mode 100644 apps/client/src/app/pages/portfolio/holdings/holdings-page.module.ts delete mode 100644 apps/client/src/app/pages/portfolio/holdings/holdings-page.scss diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ba54382..a744b8e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Moved the holdings table to the holdings tab of the home page - Improved the performance labels (with and without currency effects) in the position detail dialog ## 2.78.0 - 2024-05-02 diff --git a/apps/api/src/app/portfolio/portfolio.controller.ts b/apps/api/src/app/portfolio/portfolio.controller.ts index 0828fb3e..7aa8e915 100644 --- a/apps/api/src/app/portfolio/portfolio.controller.ts +++ b/apps/api/src/app/portfolio/portfolio.controller.ts @@ -490,6 +490,9 @@ export class PortfolioController { return performanceInformation; } + /** + * @deprecated + */ @Get('positions') @UseGuards(AuthGuard('jwt'), HasPermissionGuard) @UseInterceptors(RedactValuesInResponseInterceptor) diff --git a/apps/client/src/app/components/home-holdings/home-holdings.component.ts b/apps/client/src/app/components/home-holdings/home-holdings.component.ts index 9dbf9d9b..1a556e6f 100644 --- a/apps/client/src/app/components/home-holdings/home-holdings.component.ts +++ b/apps/client/src/app/components/home-holdings/home-holdings.component.ts @@ -1,11 +1,11 @@ -import { PositionDetailDialog } from '@ghostfolio/client/components/position/position-detail-dialog/position-detail-dialog.component'; -import { ToggleComponent } from '@ghostfolio/client/components/toggle/toggle.component'; +import { PositionDetailDialogParams } from '@ghostfolio/client/components/position-detail-dialog/interfaces/interfaces'; +import { PositionDetailDialog } from '@ghostfolio/client/components/position-detail-dialog/position-detail-dialog.component'; import { DataService } from '@ghostfolio/client/services/data.service'; import { ImpersonationStorageService } from '@ghostfolio/client/services/impersonation-storage.service'; import { UserService } from '@ghostfolio/client/services/user/user.service'; -import { Position, User } from '@ghostfolio/common/interfaces'; +import { PortfolioPosition, User } from '@ghostfolio/common/interfaces'; import { hasPermission, permissions } from '@ghostfolio/common/permissions'; -import { DateRange } from '@ghostfolio/common/types'; +import { HoldingType, ToggleOption } from '@ghostfolio/common/types'; import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; @@ -15,19 +15,21 @@ import { DeviceDetectorService } from 'ngx-device-detector'; import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; -import { PositionDetailDialogParams } from '../position/position-detail-dialog/interfaces/interfaces'; - @Component({ selector: 'gf-home-holdings', styleUrls: ['./home-holdings.scss'], templateUrl: './home-holdings.html' }) export class HomeHoldingsComponent implements OnDestroy, OnInit { - public dateRangeOptions = ToggleComponent.DEFAULT_DATE_RANGE_OPTIONS; public deviceType: string; public hasImpersonationId: boolean; public hasPermissionToCreateOrder: boolean; - public positions: Position[]; + public holdings: PortfolioPosition[]; + public holdingType: HoldingType = 'ACTIVE'; + public holdingTypeOptions: ToggleOption[] = [ + { label: $localize`Active`, value: 'ACTIVE' }, + { label: $localize`Closed`, value: 'CLOSED' } + ]; public user: User; private unsubscribeSubject = new Subject(); @@ -56,6 +58,17 @@ export class HomeHoldingsComponent implements OnDestroy, OnInit { }); } }); + } + + public ngOnInit() { + this.deviceType = this.deviceService.getDeviceInfo().deviceType; + + this.impersonationStorageService + .onChangeHasImpersonation() + .pipe(takeUntil(this.unsubscribeSubject)) + .subscribe((impersonationId) => { + this.hasImpersonationId = !!impersonationId; + }); this.userService.stateChanged .pipe(takeUntil(this.unsubscribeSubject)) @@ -68,37 +81,32 @@ export class HomeHoldingsComponent implements OnDestroy, OnInit { permissions.createOrder ); - this.update(); + this.holdings = undefined; + + this.fetchHoldings() + .pipe(takeUntil(this.unsubscribeSubject)) + .subscribe(({ holdings }) => { + this.holdings = holdings; + + this.changeDetectorRef.markForCheck(); + }); + + this.changeDetectorRef.markForCheck(); } }); } - public ngOnInit() { - this.deviceType = this.deviceService.getDeviceInfo().deviceType; + public onChangeHoldingType(aHoldingType: HoldingType) { + this.holdingType = aHoldingType; - this.impersonationStorageService - .onChangeHasImpersonation() + this.holdings = undefined; + + this.fetchHoldings() .pipe(takeUntil(this.unsubscribeSubject)) - .subscribe((impersonationId) => { - this.hasImpersonationId = !!impersonationId; - }); - } + .subscribe(({ holdings }) => { + this.holdings = holdings; - public onChangeDateRange(dateRange: DateRange) { - this.dataService - .putUserSetting({ dateRange }) - .pipe(takeUntil(this.unsubscribeSubject)) - .subscribe(() => { - this.userService.remove(); - - this.userService - .get() - .pipe(takeUntil(this.unsubscribeSubject)) - .subscribe((user) => { - this.user = user; - - this.changeDetectorRef.markForCheck(); - }); + this.changeDetectorRef.markForCheck(); }); } @@ -107,6 +115,19 @@ export class HomeHoldingsComponent implements OnDestroy, OnInit { this.unsubscribeSubject.complete(); } + private fetchHoldings() { + const filters = this.userService.getFilters(); + + if (this.holdingType === 'CLOSED') { + filters.push({ id: 'CLOSED', type: 'HOLDING_TYPE' }); + } + + return this.dataService.fetchPortfolioHoldings({ + filters, + range: this.user?.settings?.dateRange + }); + } + private openPositionDialog({ dataSource, symbol @@ -147,19 +168,4 @@ export class HomeHoldingsComponent implements OnDestroy, OnInit { }); }); } - - private update() { - this.positions = undefined; - - this.dataService - .fetchPositions({ range: this.user?.settings?.dateRange }) - .pipe(takeUntil(this.unsubscribeSubject)) - .subscribe(({ positions }) => { - this.positions = positions; - - this.changeDetectorRef.markForCheck(); - }); - - this.changeDetectorRef.markForCheck(); - } } diff --git a/apps/client/src/app/components/home-holdings/home-holdings.html b/apps/client/src/app/components/home-holdings/home-holdings.html index 72328eac..a2bd4363 100644 --- a/apps/client/src/app/components/home-holdings/home-holdings.html +++ b/apps/client/src/app/components/home-holdings/home-holdings.html @@ -1,27 +1,38 @@ -
+
-
- - - - - -
- Manage Activities +
+

Holdings

+
+
+
+
+
+
+ + @if (hasPermissionToCreateOrder && holdings?.length > 0) { + + }
diff --git a/apps/client/src/app/components/home-holdings/home-holdings.module.ts b/apps/client/src/app/components/home-holdings/home-holdings.module.ts index b6fa70e8..f10adeab 100644 --- a/apps/client/src/app/components/home-holdings/home-holdings.module.ts +++ b/apps/client/src/app/components/home-holdings/home-holdings.module.ts @@ -1,11 +1,9 @@ -import { GfPositionDetailDialogModule } from '@ghostfolio/client/components/position/position-detail-dialog/position-detail-dialog.module'; -import { GfPositionsModule } from '@ghostfolio/client/components/positions/positions.module'; import { GfToggleModule } from '@ghostfolio/client/components/toggle/toggle.module'; +import { GfHoldingsTableComponent } from '@ghostfolio/ui/holdings-table'; import { CommonModule } from '@angular/common'; import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; -import { MatCardModule } from '@angular/material/card'; import { RouterModule } from '@angular/router'; import { HomeHoldingsComponent } from './home-holdings.component'; @@ -14,11 +12,9 @@ import { HomeHoldingsComponent } from './home-holdings.component'; declarations: [HomeHoldingsComponent], imports: [ CommonModule, - GfPositionDetailDialogModule, - GfPositionsModule, + GfHoldingsTableComponent, GfToggleModule, MatButtonModule, - MatCardModule, RouterModule ], schemas: [CUSTOM_ELEMENTS_SCHEMA] diff --git a/apps/client/src/app/components/position/position-detail-dialog/interfaces/interfaces.ts b/apps/client/src/app/components/position-detail-dialog/interfaces/interfaces.ts similarity index 100% rename from apps/client/src/app/components/position/position-detail-dialog/interfaces/interfaces.ts rename to apps/client/src/app/components/position-detail-dialog/interfaces/interfaces.ts diff --git a/apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.component.scss b/apps/client/src/app/components/position-detail-dialog/position-detail-dialog.component.scss similarity index 100% rename from apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.component.scss rename to apps/client/src/app/components/position-detail-dialog/position-detail-dialog.component.scss diff --git a/apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.component.ts b/apps/client/src/app/components/position-detail-dialog/position-detail-dialog.component.ts similarity index 100% rename from apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.component.ts rename to apps/client/src/app/components/position-detail-dialog/position-detail-dialog.component.ts diff --git a/apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.html b/apps/client/src/app/components/position-detail-dialog/position-detail-dialog.html similarity index 100% rename from apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.html rename to apps/client/src/app/components/position-detail-dialog/position-detail-dialog.html diff --git a/apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.module.ts b/apps/client/src/app/components/position-detail-dialog/position-detail-dialog.module.ts similarity index 100% rename from apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.module.ts rename to apps/client/src/app/components/position-detail-dialog/position-detail-dialog.module.ts diff --git a/apps/client/src/app/components/position/position.component.html b/apps/client/src/app/components/position/position.component.html deleted file mode 100644 index 4a5ed6f9..00000000 --- a/apps/client/src/app/components/position/position.component.html +++ /dev/null @@ -1,72 +0,0 @@ - diff --git a/apps/client/src/app/components/position/position.component.scss b/apps/client/src/app/components/position/position.component.scss deleted file mode 100644 index 7044d779..00000000 --- a/apps/client/src/app/components/position/position.component.scss +++ /dev/null @@ -1,13 +0,0 @@ -:host { - display: block; - - .container { - gf-trend-indicator { - padding-top: 0.15rem; - } - - .chevron { - opacity: 0.33; - } - } -} diff --git a/apps/client/src/app/components/position/position.component.ts b/apps/client/src/app/components/position/position.component.ts deleted file mode 100644 index 3a5fbae8..00000000 --- a/apps/client/src/app/components/position/position.component.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { UNKNOWN_KEY } from '@ghostfolio/common/config'; -import { getLocale } from '@ghostfolio/common/helper'; -import { Position } from '@ghostfolio/common/interfaces'; - -import { - ChangeDetectionStrategy, - Component, - Input, - OnDestroy, - OnInit -} from '@angular/core'; -import { Subject } from 'rxjs'; - -@Component({ - selector: 'gf-position', - changeDetection: ChangeDetectionStrategy.OnPush, - templateUrl: './position.component.html', - styleUrls: ['./position.component.scss'] -}) -export class PositionComponent implements OnDestroy, OnInit { - @Input() baseCurrency: string; - @Input() deviceType: string; - @Input() isLoading: boolean; - @Input() locale = getLocale(); - @Input() position: Position; - @Input() range: string; - - public unknownKey = UNKNOWN_KEY; - - private unsubscribeSubject = new Subject(); - - public constructor() {} - - public ngOnInit() {} - - public ngOnDestroy() { - this.unsubscribeSubject.next(); - this.unsubscribeSubject.complete(); - } -} diff --git a/apps/client/src/app/components/position/position.module.ts b/apps/client/src/app/components/position/position.module.ts deleted file mode 100644 index 6483e274..00000000 --- a/apps/client/src/app/components/position/position.module.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { GfSymbolModule } from '@ghostfolio/client/pipes/symbol/symbol.module'; -import { GfTrendIndicatorComponent } from '@ghostfolio/ui/trend-indicator'; -import { GfValueComponent } from '@ghostfolio/ui/value'; - -import { CommonModule } from '@angular/common'; -import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; -import { MatDialogModule } from '@angular/material/dialog'; -import { RouterModule } from '@angular/router'; -import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; - -import { GfPositionDetailDialogModule } from './position-detail-dialog/position-detail-dialog.module'; -import { PositionComponent } from './position.component'; - -@NgModule({ - declarations: [PositionComponent], - exports: [PositionComponent], - imports: [ - CommonModule, - GfPositionDetailDialogModule, - GfSymbolModule, - GfTrendIndicatorComponent, - GfValueComponent, - MatDialogModule, - NgxSkeletonLoaderModule, - RouterModule - ], - schemas: [CUSTOM_ELEMENTS_SCHEMA] -}) -export class GfPositionModule {} diff --git a/apps/client/src/app/components/positions/positions.component.html b/apps/client/src/app/components/positions/positions.component.html deleted file mode 100644 index 606c5921..00000000 --- a/apps/client/src/app/components/positions/positions.component.html +++ /dev/null @@ -1,35 +0,0 @@ -
-
-
- - - - - - - - -
- -
-
-
-
-
diff --git a/apps/client/src/app/components/positions/positions.component.scss b/apps/client/src/app/components/positions/positions.component.scss deleted file mode 100644 index d3e8995a..00000000 --- a/apps/client/src/app/components/positions/positions.component.scss +++ /dev/null @@ -1,17 +0,0 @@ -:host { - display: block; - - gf-position { - &:nth-child(even) { - background-color: rgba(0, 0, 0, var(--gf-theme-alpha-hover)); - } - } -} - -:host-context(.is-dark-theme) { - gf-position { - &:nth-child(even) { - background-color: rgba(255, 255, 255, var(--gf-theme-alpha-hover)); - } - } -} diff --git a/apps/client/src/app/components/positions/positions.component.ts b/apps/client/src/app/components/positions/positions.component.ts deleted file mode 100644 index ab981246..00000000 --- a/apps/client/src/app/components/positions/positions.component.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { getLocale } from '@ghostfolio/common/helper'; -import { Position } from '@ghostfolio/common/interfaces'; - -import { - ChangeDetectionStrategy, - Component, - Input, - OnChanges, - OnInit -} from '@angular/core'; - -@Component({ - selector: 'gf-positions', - changeDetection: ChangeDetectionStrategy.OnPush, - templateUrl: './positions.component.html', - styleUrls: ['./positions.component.scss'] -}) -export class PositionsComponent implements OnChanges, OnInit { - @Input() baseCurrency: string; - @Input() deviceType: string; - @Input() hasPermissionToCreateOrder: boolean; - @Input() locale = getLocale(); - @Input() positions: Position[]; - @Input() range: string; - - public hasPositions: boolean; - public positionsRest: Position[] = []; - public positionsWithPriority: Position[] = []; - - public constructor() {} - - public ngOnInit() {} - - public ngOnChanges() { - if (this.positions) { - this.hasPositions = this.positions.length > 0; - - if (!this.hasPositions) { - return; - } - - this.positionsRest = []; - this.positionsWithPriority = []; - - for (const portfolioPosition of this.positions) { - if (portfolioPosition.marketState === 'open' || this.range !== '1d') { - // Only show positions where the market is open in today's view - this.positionsWithPriority.push(portfolioPosition); - } else { - this.positionsRest.push(portfolioPosition); - } - } - - this.positionsRest.sort((a, b) => - (a.name || a.symbol)?.toLowerCase() > - (b.name || b.symbol)?.toLowerCase() - ? 1 - : -1 - ); - this.positionsWithPriority.sort((a, b) => - (a.name || a.symbol)?.toLowerCase() > - (b.name || b.symbol)?.toLowerCase() - ? 1 - : -1 - ); - } else { - this.hasPositions = false; - } - } -} diff --git a/apps/client/src/app/components/positions/positions.module.ts b/apps/client/src/app/components/positions/positions.module.ts deleted file mode 100644 index 34bd38b2..00000000 --- a/apps/client/src/app/components/positions/positions.module.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { GfNoTransactionsInfoComponent } from '@ghostfolio/ui/no-transactions-info'; - -import { CommonModule } from '@angular/common'; -import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; -import { MatButtonModule } from '@angular/material/button'; - -import { GfPositionModule } from '../position/position.module'; -import { PositionsComponent } from './positions.component'; - -@NgModule({ - declarations: [PositionsComponent], - exports: [PositionsComponent], - imports: [ - CommonModule, - GfNoTransactionsInfoComponent, - GfPositionModule, - MatButtonModule - ], - schemas: [CUSTOM_ELEMENTS_SCHEMA] -}) -export class GfPositionsModule {} diff --git a/apps/client/src/app/components/rules/rules.module.ts b/apps/client/src/app/components/rules/rules.module.ts index 3b82c6ab..26ed1d83 100644 --- a/apps/client/src/app/components/rules/rules.module.ts +++ b/apps/client/src/app/components/rules/rules.module.ts @@ -6,7 +6,6 @@ import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; -import { GfPositionModule } from '../position/position.module'; import { RulesComponent } from './rules.component'; @NgModule({ @@ -15,7 +14,6 @@ import { RulesComponent } from './rules.component'; imports: [ CommonModule, GfNoTransactionsInfoComponent, - GfPositionModule, GfRuleModule, MatButtonModule, MatCardModule diff --git a/apps/client/src/app/pages/home/home-page-routing.module.ts b/apps/client/src/app/pages/home/home-page-routing.module.ts index bccfc2f5..f50b5519 100644 --- a/apps/client/src/app/pages/home/home-page-routing.module.ts +++ b/apps/client/src/app/pages/home/home-page-routing.module.ts @@ -22,6 +22,11 @@ const routes: Routes = [ component: HomeHoldingsComponent, title: $localize`Holdings` }, + { + path: 'holdings', + component: HomeHoldingsComponent, + title: $localize`Holdings` + }, { path: 'summary', component: HomeSummaryComponent, diff --git a/apps/client/src/app/pages/portfolio/activities/activities-page.component.ts b/apps/client/src/app/pages/portfolio/activities/activities-page.component.ts index f6f0fede..6e66bb66 100644 --- a/apps/client/src/app/pages/portfolio/activities/activities-page.component.ts +++ b/apps/client/src/app/pages/portfolio/activities/activities-page.component.ts @@ -1,8 +1,8 @@ import { CreateOrderDto } from '@ghostfolio/api/app/order/create-order.dto'; import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface'; import { UpdateOrderDto } from '@ghostfolio/api/app/order/update-order.dto'; -import { PositionDetailDialogParams } from '@ghostfolio/client/components/position/position-detail-dialog/interfaces/interfaces'; -import { PositionDetailDialog } from '@ghostfolio/client/components/position/position-detail-dialog/position-detail-dialog.component'; +import { PositionDetailDialogParams } from '@ghostfolio/client/components/position-detail-dialog/interfaces/interfaces'; +import { PositionDetailDialog } from '@ghostfolio/client/components/position-detail-dialog/position-detail-dialog.component'; import { DataService } from '@ghostfolio/client/services/data.service'; import { IcsService } from '@ghostfolio/client/services/ics/ics.service'; import { ImpersonationStorageService } from '@ghostfolio/client/services/impersonation-storage.service'; diff --git a/apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts b/apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts index 6d3aed4d..0172c809 100644 --- a/apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts +++ b/apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts @@ -1,7 +1,7 @@ import { AccountDetailDialog } from '@ghostfolio/client/components/account-detail-dialog/account-detail-dialog.component'; import { AccountDetailDialogParams } from '@ghostfolio/client/components/account-detail-dialog/interfaces/interfaces'; -import { PositionDetailDialogParams } from '@ghostfolio/client/components/position/position-detail-dialog/interfaces/interfaces'; -import { PositionDetailDialog } from '@ghostfolio/client/components/position/position-detail-dialog/position-detail-dialog.component'; +import { PositionDetailDialogParams } from '@ghostfolio/client/components/position-detail-dialog/interfaces/interfaces'; +import { PositionDetailDialog } from '@ghostfolio/client/components/position-detail-dialog/position-detail-dialog.component'; import { DataService } from '@ghostfolio/client/services/data.service'; import { ImpersonationStorageService } from '@ghostfolio/client/services/impersonation-storage.service'; import { UserService } from '@ghostfolio/client/services/user/user.service'; @@ -103,7 +103,7 @@ export class AllocationsPageComponent implements OnDestroy, OnInit { private router: Router, private userService: UserService ) { - route.queryParams + this.route.queryParams .pipe(takeUntil(this.unsubscribeSubject)) .subscribe((params) => { if (params['accountId'] && params['accountDetailDialog']) { diff --git a/apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts b/apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts index 184297b2..4acf6dbb 100644 --- a/apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts +++ b/apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts @@ -1,5 +1,5 @@ -import { PositionDetailDialogParams } from '@ghostfolio/client/components/position/position-detail-dialog/interfaces/interfaces'; -import { PositionDetailDialog } from '@ghostfolio/client/components/position/position-detail-dialog/position-detail-dialog.component'; +import { PositionDetailDialogParams } from '@ghostfolio/client/components/position-detail-dialog/interfaces/interfaces'; +import { PositionDetailDialog } from '@ghostfolio/client/components/position-detail-dialog/position-detail-dialog.component'; import { ToggleComponent } from '@ghostfolio/client/components/toggle/toggle.component'; import { DataService } from '@ghostfolio/client/services/data.service'; import { ImpersonationStorageService } from '@ghostfolio/client/services/impersonation-storage.service'; @@ -80,7 +80,7 @@ export class AnalysisPageComponent implements OnDestroy, OnInit { const { benchmarks } = this.dataService.fetchInfo(); this.benchmarks = benchmarks; - route.queryParams + this.route.queryParams .pipe(takeUntil(this.unsubscribeSubject)) .subscribe((params) => { if ( diff --git a/apps/client/src/app/pages/portfolio/holdings/holdings-page-routing.module.ts b/apps/client/src/app/pages/portfolio/holdings/holdings-page-routing.module.ts deleted file mode 100644 index 94b49a9d..00000000 --- a/apps/client/src/app/pages/portfolio/holdings/holdings-page-routing.module.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { AuthGuard } from '@ghostfolio/client/core/auth.guard'; - -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; - -import { HoldingsPageComponent } from './holdings-page.component'; - -const routes: Routes = [ - { - canActivate: [AuthGuard], - component: HoldingsPageComponent, - path: '', - title: $localize`Holdings` - } -]; - -@NgModule({ - imports: [RouterModule.forChild(routes)], - exports: [RouterModule] -}) -export class HoldingsPageRoutingModule {} diff --git a/apps/client/src/app/pages/portfolio/holdings/holdings-page.component.ts b/apps/client/src/app/pages/portfolio/holdings/holdings-page.component.ts deleted file mode 100644 index 107e8f30..00000000 --- a/apps/client/src/app/pages/portfolio/holdings/holdings-page.component.ts +++ /dev/null @@ -1,171 +0,0 @@ -import { PositionDetailDialogParams } from '@ghostfolio/client/components/position/position-detail-dialog/interfaces/interfaces'; -import { PositionDetailDialog } from '@ghostfolio/client/components/position/position-detail-dialog/position-detail-dialog.component'; -import { DataService } from '@ghostfolio/client/services/data.service'; -import { ImpersonationStorageService } from '@ghostfolio/client/services/impersonation-storage.service'; -import { UserService } from '@ghostfolio/client/services/user/user.service'; -import { PortfolioPosition, User } from '@ghostfolio/common/interfaces'; -import { hasPermission, permissions } from '@ghostfolio/common/permissions'; -import { HoldingType, ToggleOption } from '@ghostfolio/common/types'; - -import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; -import { MatDialog } from '@angular/material/dialog'; -import { ActivatedRoute, Router } from '@angular/router'; -import { DataSource } from '@prisma/client'; -import { DeviceDetectorService } from 'ngx-device-detector'; -import { Subject } from 'rxjs'; -import { takeUntil } from 'rxjs/operators'; - -@Component({ - selector: 'gf-holdings-page', - styleUrls: ['./holdings-page.scss'], - templateUrl: './holdings-page.html' -}) -export class HoldingsPageComponent implements OnDestroy, OnInit { - public deviceType: string; - public hasImpersonationId: boolean; - public hasPermissionToCreateOrder: boolean; - public holdings: PortfolioPosition[]; - public holdingType: HoldingType = 'ACTIVE'; - public holdingTypeOptions: ToggleOption[] = [ - { label: $localize`Active`, value: 'ACTIVE' }, - { label: $localize`Closed`, value: 'CLOSED' } - ]; - public user: User; - - private unsubscribeSubject = new Subject(); - - public constructor( - private changeDetectorRef: ChangeDetectorRef, - private dataService: DataService, - private deviceService: DeviceDetectorService, - private dialog: MatDialog, - private impersonationStorageService: ImpersonationStorageService, - private route: ActivatedRoute, - private router: Router, - private userService: UserService - ) { - route.queryParams - .pipe(takeUntil(this.unsubscribeSubject)) - .subscribe((params) => { - if ( - params['dataSource'] && - params['positionDetailDialog'] && - params['symbol'] - ) { - this.openPositionDialog({ - dataSource: params['dataSource'], - symbol: params['symbol'] - }); - } - }); - } - - public ngOnInit() { - this.deviceType = this.deviceService.getDeviceInfo().deviceType; - - this.impersonationStorageService - .onChangeHasImpersonation() - .pipe(takeUntil(this.unsubscribeSubject)) - .subscribe((impersonationId) => { - this.hasImpersonationId = !!impersonationId; - }); - - this.userService.stateChanged - .pipe(takeUntil(this.unsubscribeSubject)) - .subscribe((state) => { - if (state?.user) { - this.user = state.user; - - this.hasPermissionToCreateOrder = hasPermission( - this.user.permissions, - permissions.createOrder - ); - - this.holdings = undefined; - - this.fetchHoldings() - .pipe(takeUntil(this.unsubscribeSubject)) - .subscribe(({ holdings }) => { - this.holdings = holdings; - - this.changeDetectorRef.markForCheck(); - }); - - this.changeDetectorRef.markForCheck(); - } - }); - } - - public onChangeHoldingType(aHoldingType: HoldingType) { - this.holdingType = aHoldingType; - - this.holdings = undefined; - - this.fetchHoldings() - .pipe(takeUntil(this.unsubscribeSubject)) - .subscribe(({ holdings }) => { - this.holdings = holdings; - - this.changeDetectorRef.markForCheck(); - }); - } - - public ngOnDestroy() { - this.unsubscribeSubject.next(); - this.unsubscribeSubject.complete(); - } - - private fetchHoldings() { - const filters = this.userService.getFilters(); - - if (this.holdingType === 'CLOSED') { - filters.push({ id: 'CLOSED', type: 'HOLDING_TYPE' }); - } - - return this.dataService.fetchPortfolioHoldings({ - filters, - range: this.user?.settings?.dateRange - }); - } - - private openPositionDialog({ - dataSource, - symbol - }: { - dataSource: DataSource; - symbol: string; - }) { - this.userService - .get() - .pipe(takeUntil(this.unsubscribeSubject)) - .subscribe((user) => { - this.user = user; - - const dialogRef = this.dialog.open(PositionDetailDialog, { - autoFocus: false, - data: { - dataSource, - symbol, - baseCurrency: this.user?.settings?.baseCurrency, - colorScheme: this.user?.settings?.colorScheme, - deviceType: this.deviceType, - hasImpersonationId: this.hasImpersonationId, - hasPermissionToReportDataGlitch: hasPermission( - this.user?.permissions, - permissions.reportDataGlitch - ), - locale: this.user?.settings?.locale - }, - height: this.deviceType === 'mobile' ? '97.5vh' : '80vh', - width: this.deviceType === 'mobile' ? '100vw' : '50rem' - }); - - dialogRef - .afterClosed() - .pipe(takeUntil(this.unsubscribeSubject)) - .subscribe(() => { - this.router.navigate(['.'], { relativeTo: this.route }); - }); - }); - } -} diff --git a/apps/client/src/app/pages/portfolio/holdings/holdings-page.html b/apps/client/src/app/pages/portfolio/holdings/holdings-page.html deleted file mode 100644 index a2bd4363..00000000 --- a/apps/client/src/app/pages/portfolio/holdings/holdings-page.html +++ /dev/null @@ -1,38 +0,0 @@ -
-
-
-

Holdings

-
-
-
-
-
- -
- - @if (hasPermissionToCreateOrder && holdings?.length > 0) { - - } -
-
-
diff --git a/apps/client/src/app/pages/portfolio/holdings/holdings-page.module.ts b/apps/client/src/app/pages/portfolio/holdings/holdings-page.module.ts deleted file mode 100644 index a5040f37..00000000 --- a/apps/client/src/app/pages/portfolio/holdings/holdings-page.module.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { GfToggleModule } from '@ghostfolio/client/components/toggle/toggle.module'; -import { GfHoldingsTableComponent } from '@ghostfolio/ui/holdings-table'; - -import { CommonModule } from '@angular/common'; -import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; -import { MatButtonModule } from '@angular/material/button'; - -import { HoldingsPageRoutingModule } from './holdings-page-routing.module'; -import { HoldingsPageComponent } from './holdings-page.component'; - -@NgModule({ - declarations: [HoldingsPageComponent], - imports: [ - CommonModule, - GfHoldingsTableComponent, - GfToggleModule, - HoldingsPageRoutingModule, - MatButtonModule - ], - schemas: [CUSTOM_ELEMENTS_SCHEMA] -}) -export class HoldingsPageModule {} diff --git a/apps/client/src/app/pages/portfolio/holdings/holdings-page.scss b/apps/client/src/app/pages/portfolio/holdings/holdings-page.scss deleted file mode 100644 index 5d4e87f3..00000000 --- a/apps/client/src/app/pages/portfolio/holdings/holdings-page.scss +++ /dev/null @@ -1,3 +0,0 @@ -:host { - display: block; -} diff --git a/apps/client/src/app/pages/portfolio/portfolio-page-routing.module.ts b/apps/client/src/app/pages/portfolio/portfolio-page-routing.module.ts index d4f93b56..6146c573 100644 --- a/apps/client/src/app/pages/portfolio/portfolio-page-routing.module.ts +++ b/apps/client/src/app/pages/portfolio/portfolio-page-routing.module.ts @@ -16,13 +16,6 @@ const routes: Routes = [ (m) => m.AnalysisPageModule ) }, - { - path: 'holdings', - loadChildren: () => - import('./holdings/holdings-page.module').then( - (m) => m.HoldingsPageModule - ) - }, { path: 'activities', loadChildren: () => diff --git a/apps/client/src/app/pages/portfolio/portfolio-page.component.ts b/apps/client/src/app/pages/portfolio/portfolio-page.component.ts index bbd70c1c..0c980e25 100644 --- a/apps/client/src/app/pages/portfolio/portfolio-page.component.ts +++ b/apps/client/src/app/pages/portfolio/portfolio-page.component.ts @@ -34,11 +34,6 @@ export class PortfolioPageComponent implements OnDestroy, OnInit { label: $localize`Analysis`, path: ['/portfolio'] }, - { - iconName: 'wallet-outline', - label: $localize`Holdings`, - path: ['/portfolio', 'holdings'] - }, { iconName: 'swap-vertical-outline', label: $localize`Activities`, diff --git a/apps/client/src/app/services/data.service.ts b/apps/client/src/app/services/data.service.ts index 16d10483..d5c1bec0 100644 --- a/apps/client/src/app/services/data.service.ts +++ b/apps/client/src/app/services/data.service.ts @@ -376,6 +376,9 @@ export class DataService { }); } + /** + * @deprecated + */ public fetchPositions({ filters, range diff --git a/libs/ui/src/lib/holdings-table/holdings-table.component.ts b/libs/ui/src/lib/holdings-table/holdings-table.component.ts index 93ac5b6f..f2523927 100644 --- a/libs/ui/src/lib/holdings-table/holdings-table.component.ts +++ b/libs/ui/src/lib/holdings-table/holdings-table.component.ts @@ -1,5 +1,5 @@ import { GfAssetProfileIconComponent } from '@ghostfolio/client/components/asset-profile-icon/asset-profile-icon.component'; -import { GfPositionDetailDialogModule } from '@ghostfolio/client/components/position/position-detail-dialog/position-detail-dialog.module'; +import { GfPositionDetailDialogModule } from '@ghostfolio/client/components/position-detail-dialog/position-detail-dialog.module'; import { GfSymbolModule } from '@ghostfolio/client/pipes/symbol/symbol.module'; import { getLocale } from '@ghostfolio/common/helper'; import { PortfolioPosition, UniqueAsset } from '@ghostfolio/common/interfaces';