diff --git a/CHANGELOG.md b/CHANGELOG.md index 4941721e2..537c6d89c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Improved the usability of the _Cancel_ / _Close_ buttons in the create watchlist item dialog +- Refactored the `fireWealth` from `number` type to a structured object in the summary of the portfolio details endpoint +- Refactored the _Open Startup_ (`/open`) page to standalone +- Refactored the file drop directive to standalone - Refactored the symbol pipe to standalone ### Fixed diff --git a/apps/api/src/app/portfolio/portfolio.controller.ts b/apps/api/src/app/portfolio/portfolio.controller.ts index d703cf604..5659818a8 100644 --- a/apps/api/src/app/portfolio/portfolio.controller.ts +++ b/apps/api/src/app/portfolio/portfolio.controller.ts @@ -195,7 +195,6 @@ export class PortfolioController { 'excludedAccountsAndActivities', 'fees', 'filteredValueInBaseCurrency', - 'fireWealth', 'grossPerformance', 'grossPerformanceWithCurrencyEffect', 'interest', diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts index 8415e24a5..f1f53d435 100644 --- a/apps/api/src/app/portfolio/portfolio.service.ts +++ b/apps/api/src/app/portfolio/portfolio.service.ts @@ -2138,9 +2138,13 @@ export class PortfolioService { filteredValueInPercentage: netWorth ? filteredValueInBaseCurrency.div(netWorth).toNumber() : undefined, - fireWealth: new Big(currentValueInBaseCurrency) - .minus(emergencyFundHoldingsValueInBaseCurrency) - .toNumber(), + fireWealth: { + today: { + valueInBaseCurrency: new Big(currentValueInBaseCurrency) + .minus(emergencyFundHoldingsValueInBaseCurrency) + .toNumber() + } + }, grossPerformance: new Big(netPerformance).plus(fees).toNumber(), grossPerformanceWithCurrencyEffect: new Big( netPerformanceWithCurrencyEffect diff --git a/apps/client/src/app/app-routing.module.ts b/apps/client/src/app/app-routing.module.ts index 53db1f06b..5c5eadcab 100644 --- a/apps/client/src/app/app-routing.module.ts +++ b/apps/client/src/app/app-routing.module.ts @@ -94,7 +94,7 @@ const routes: Routes = [ { path: publicRoutes.openStartup.path, loadChildren: () => - import('./pages/open/open-page.module').then((m) => m.OpenPageModule) + import('./pages/open/open-page.routes').then((m) => m.routes) }, { path: internalRoutes.portfolio.path, diff --git a/apps/client/src/app/directives/file-drop/file-drop.directive.ts b/apps/client/src/app/directives/file-drop/file-drop.directive.ts index 93f444572..a7e628bc9 100644 --- a/apps/client/src/app/directives/file-drop/file-drop.directive.ts +++ b/apps/client/src/app/directives/file-drop/file-drop.directive.ts @@ -1,10 +1,9 @@ import { Directive, EventEmitter, HostListener, Output } from '@angular/core'; @Directive({ - selector: '[gfFileDrop]', - standalone: false + selector: '[gfFileDrop]' }) -export class FileDropDirective { +export class GfFileDropDirective { @Output() filesDropped = new EventEmitter(); @HostListener('dragenter', ['$event']) onDragEnter(event: DragEvent) { diff --git a/apps/client/src/app/directives/file-drop/file-drop.module.ts b/apps/client/src/app/directives/file-drop/file-drop.module.ts deleted file mode 100644 index a0148516e..000000000 --- a/apps/client/src/app/directives/file-drop/file-drop.module.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { NgModule } from '@angular/core'; - -import { FileDropDirective } from './file-drop.directive'; - -@NgModule({ - declarations: [FileDropDirective], - exports: [FileDropDirective] -}) -export class GfFileDropModule {} diff --git a/apps/client/src/app/pages/open/open-page-routing.module.ts b/apps/client/src/app/pages/open/open-page-routing.module.ts deleted file mode 100644 index c56fe80ba..000000000 --- a/apps/client/src/app/pages/open/open-page-routing.module.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { AuthGuard } from '@ghostfolio/client/core/auth.guard'; -import { publicRoutes } from '@ghostfolio/common/routes/routes'; - -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; - -import { OpenPageComponent } from './open-page.component'; - -const routes: Routes = [ - { - canActivate: [AuthGuard], - component: OpenPageComponent, - path: '', - title: publicRoutes.openStartup.title - } -]; - -@NgModule({ - imports: [RouterModule.forChild(routes)], - exports: [RouterModule] -}) -export class OpenPageRoutingModule {} diff --git a/apps/client/src/app/pages/open/open-page.component.ts b/apps/client/src/app/pages/open/open-page.component.ts index c876c3cc8..6521951da 100644 --- a/apps/client/src/app/pages/open/open-page.component.ts +++ b/apps/client/src/app/pages/open/open-page.component.ts @@ -1,18 +1,28 @@ import { DataService } from '@ghostfolio/client/services/data.service'; import { UserService } from '@ghostfolio/client/services/user/user.service'; import { Statistics, User } from '@ghostfolio/common/interfaces'; - -import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; +import { GfValueComponent } from '@ghostfolio/ui/value'; + +import { CommonModule } from '@angular/common'; +import { + ChangeDetectorRef, + Component, + CUSTOM_ELEMENTS_SCHEMA, + OnDestroy, + OnInit +} from '@angular/core'; +import { MatCardModule } from '@angular/material/card'; import { Subject, takeUntil } from 'rxjs'; @Component({ host: { class: 'page' }, + imports: [CommonModule, GfValueComponent, MatCardModule], + schemas: [CUSTOM_ELEMENTS_SCHEMA], selector: 'gf-open-page', styleUrls: ['./open-page.scss'], - templateUrl: './open-page.html', - standalone: false + templateUrl: './open-page.html' }) -export class OpenPageComponent implements OnDestroy, OnInit { +export class GfOpenPageComponent implements OnDestroy, OnInit { public statistics: Statistics; public user: User; diff --git a/apps/client/src/app/pages/open/open-page.module.ts b/apps/client/src/app/pages/open/open-page.module.ts deleted file mode 100644 index ab48f2d4a..000000000 --- a/apps/client/src/app/pages/open/open-page.module.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { GfValueComponent } from '@ghostfolio/ui/value'; - -import { CommonModule } from '@angular/common'; -import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; -import { MatCardModule } from '@angular/material/card'; - -import { OpenPageRoutingModule } from './open-page-routing.module'; -import { OpenPageComponent } from './open-page.component'; - -@NgModule({ - declarations: [OpenPageComponent], - imports: [ - CommonModule, - GfValueComponent, - MatCardModule, - OpenPageRoutingModule - ], - schemas: [CUSTOM_ELEMENTS_SCHEMA] -}) -export class OpenPageModule {} diff --git a/apps/client/src/app/pages/open/open-page.routes.ts b/apps/client/src/app/pages/open/open-page.routes.ts new file mode 100644 index 000000000..da04d6f5b --- /dev/null +++ b/apps/client/src/app/pages/open/open-page.routes.ts @@ -0,0 +1,15 @@ +import { AuthGuard } from '@ghostfolio/client/core/auth.guard'; +import { publicRoutes } from '@ghostfolio/common/routes/routes'; + +import { Routes } from '@angular/router'; + +import { GfOpenPageComponent } from './open-page.component'; + +export const routes: Routes = [ + { + canActivate: [AuthGuard], + component: GfOpenPageComponent, + path: '', + title: publicRoutes.openStartup.title + } +]; diff --git a/apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts b/apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts index db581bcd8..d1eaf0fe1 100644 --- a/apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts +++ b/apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts @@ -4,7 +4,7 @@ import { CreateAssetProfileWithMarketDataDto } from '@ghostfolio/api/app/import/ import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface'; import { GfDialogFooterComponent } from '@ghostfolio/client/components/dialog-footer/dialog-footer.component'; import { GfDialogHeaderComponent } from '@ghostfolio/client/components/dialog-header/dialog-header.component'; -import { GfFileDropModule } from '@ghostfolio/client/directives/file-drop/file-drop.module'; +import { GfFileDropDirective } from '@ghostfolio/client/directives/file-drop/file-drop.directive'; import { GfSymbolPipe } from '@ghostfolio/client/pipes/symbol/symbol.pipe'; import { DataService } from '@ghostfolio/client/services/data.service'; import { ImportActivitiesService } from '@ghostfolio/client/services/import-activities.service'; @@ -62,7 +62,7 @@ import { ImportActivitiesDialogParams } from './interfaces/interfaces'; GfActivitiesTableComponent, GfDialogFooterComponent, GfDialogHeaderComponent, - GfFileDropModule, + GfFileDropDirective, GfSymbolPipe, IonIcon, MatButtonModule, diff --git a/apps/client/src/app/pages/portfolio/fire/fire-page.component.ts b/apps/client/src/app/pages/portfolio/fire/fire-page.component.ts index ab0fbc787..c80b55c45 100644 --- a/apps/client/src/app/pages/portfolio/fire/fire-page.component.ts +++ b/apps/client/src/app/pages/portfolio/fire/fire-page.component.ts @@ -1,7 +1,7 @@ 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 { User } from '@ghostfolio/common/interfaces'; +import { FireWealth, User } from '@ghostfolio/common/interfaces'; import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { GfFireCalculatorComponent } from '@ghostfolio/ui/fire-calculator'; import { GfPremiumIndicatorComponent } from '@ghostfolio/ui/premium-indicator'; @@ -29,7 +29,7 @@ import { takeUntil } from 'rxjs/operators'; }) export class GfFirePageComponent implements OnDestroy, OnInit { public deviceType: string; - public fireWealth: Big; + public fireWealth: FireWealth; public hasImpersonationId: boolean; public hasPermissionToUpdateUserSettings: boolean; public isLoading = false; @@ -55,17 +55,24 @@ export class GfFirePageComponent implements OnDestroy, OnInit { .fetchPortfolioDetails() .pipe(takeUntil(this.unsubscribeSubject)) .subscribe(({ summary }) => { - this.fireWealth = summary.fireWealth - ? new Big(summary.fireWealth) - : new Big(0); - + this.fireWealth = { + today: { + valueInBaseCurrency: summary.fireWealth + ? summary.fireWealth.today.valueInBaseCurrency + : 0 + } + }; if (this.user.subscription?.type === 'Basic') { - this.fireWealth = new Big(10000); + this.fireWealth = { + today: { + valueInBaseCurrency: 10000 + } + }; } - this.withdrawalRatePerYear = this.fireWealth.mul( - this.user.settings.safeWithdrawalRate - ); + this.withdrawalRatePerYear = Big( + this.fireWealth.today.valueInBaseCurrency + ).mul(this.user.settings.safeWithdrawalRate); this.withdrawalRatePerMonth = this.withdrawalRatePerYear.div(12); diff --git a/apps/client/src/app/pages/portfolio/fire/fire-page.html b/apps/client/src/app/pages/portfolio/fire/fire-page.html index df81991c3..d6548f761 100644 --- a/apps/client/src/app/pages/portfolio/fire/fire-page.html +++ b/apps/client/src/app/pages/portfolio/fire/fire-page.html @@ -14,7 +14,7 @@ [colorScheme]="user?.settings?.colorScheme" [currency]="user?.settings?.baseCurrency" [deviceType]="deviceType" - [fireWealth]="fireWealth?.toNumber()" + [fireWealth]="fireWealth?.today.valueInBaseCurrency" [hasPermissionToUpdateUserSettings]=" !hasImpersonationId && hasPermissionToUpdateUserSettings " @@ -100,7 +100,7 @@ [isCurrency]="true" [locale]="user?.settings?.locale" [unit]="user?.settings?.baseCurrency" - [value]="fireWealth?.toNumber()" + [value]="fireWealth?.today.valueInBaseCurrency" />   diff --git a/libs/common/src/lib/interfaces/fire-wealth.interface.ts b/libs/common/src/lib/interfaces/fire-wealth.interface.ts new file mode 100644 index 000000000..42fbeabd4 --- /dev/null +++ b/libs/common/src/lib/interfaces/fire-wealth.interface.ts @@ -0,0 +1,3 @@ +export interface FireWealth { + today: { valueInBaseCurrency: number }; +} diff --git a/libs/common/src/lib/interfaces/index.ts b/libs/common/src/lib/interfaces/index.ts index 6529fa3ef..1da2236e8 100644 --- a/libs/common/src/lib/interfaces/index.ts +++ b/libs/common/src/lib/interfaces/index.ts @@ -19,6 +19,7 @@ import type { EnhancedSymbolProfile } from './enhanced-symbol-profile.interface' import type { Export } from './export.interface'; import type { FilterGroup } from './filter-group.interface'; import type { Filter } from './filter.interface'; +import type { FireWealth } from './fire-wealth.interface'; import type { HistoricalDataItem } from './historical-data-item.interface'; import type { HoldingWithParents } from './holding-with-parents.interface'; import type { Holding } from './holding.interface'; @@ -104,6 +105,7 @@ export { Export, Filter, FilterGroup, + FireWealth, HistoricalDataItem, HistoricalResponse, Holding, diff --git a/libs/common/src/lib/interfaces/portfolio-summary.interface.ts b/libs/common/src/lib/interfaces/portfolio-summary.interface.ts index 419915a79..05fac0ba0 100644 --- a/libs/common/src/lib/interfaces/portfolio-summary.interface.ts +++ b/libs/common/src/lib/interfaces/portfolio-summary.interface.ts @@ -1,3 +1,4 @@ +import { FireWealth } from './fire-wealth.interface'; import { PortfolioPerformance } from './portfolio-performance.interface'; export interface PortfolioSummary extends PortfolioPerformance { @@ -16,7 +17,7 @@ export interface PortfolioSummary extends PortfolioPerformance { fees: number; filteredValueInBaseCurrency?: number; filteredValueInPercentage?: number; - fireWealth: number; + fireWealth: FireWealth; grossPerformance: number; grossPerformanceWithCurrencyEffect: number; interest: number;