Browse Source

Merge remote-tracking branch 'origin/main' into feature/enable-strict-null-checks-in-ui

pull/6264/head
Kenrick Tandrian 4 weeks ago
parent
commit
d557ddd133
  1. 32
      CHANGELOG.md
  2. 6
      README.md
  3. 5
      apps/api/src/app/export/export.service.ts
  4. 1
      apps/api/src/app/portfolio/portfolio.controller.ts
  5. 3
      apps/api/src/app/portfolio/portfolio.service.ts
  6. 2
      apps/api/src/helper/object.helper.spec.ts
  7. 2
      apps/api/src/interceptors/transform-data-source-in-response/transform-data-source-in-response.interceptor.ts
  8. 17
      apps/client/src/app/pages/about/overview/about-overview-page.component.ts
  9. 12
      apps/client/src/app/pages/about/privacy-policy/privacy-policy-page.component.ts
  10. 12
      apps/client/src/app/pages/about/terms-of-service/terms-of-service-page.component.ts
  11. 18
      apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.component.ts
  12. 17
      apps/client/src/app/pages/accounts/transfer-balance/transfer-balance-dialog.component.ts
  13. 12
      apps/client/src/app/pages/blog/blog-page.component.ts
  14. 17
      apps/client/src/app/pages/faq/faq-page.component.ts
  15. 12
      apps/client/src/app/pages/faq/self-hosting/self-hosting-page.component.ts
  16. 16
      apps/client/src/app/pages/features/features-page.component.ts
  17. 19
      apps/client/src/app/pages/home/home-page.component.ts
  18. 10
      apps/client/src/app/pages/i18n/i18n-page.component.ts
  19. 30
      apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts
  20. 22
      apps/client/src/app/pages/portfolio/portfolio-page.component.ts
  21. 17
      apps/client/src/app/pages/register/register-page.component.ts
  22. 12
      apps/client/src/app/pages/resources/personal-finance-tools/personal-finance-tools-page.component.ts
  23. 16
      apps/client/src/app/pages/user-account/user-account-page.component.ts
  24. 24
      apps/client/src/app/pages/webauthn/webauthn-page.component.ts
  25. 22
      apps/client/src/app/pages/zen/zen-page.component.ts
  26. 2
      apps/client/src/app/services/import-activities.service.ts
  27. 2
      apps/client/src/locales/messages.es.xlf
  28. 2
      apps/client/src/locales/messages.it.xlf
  29. 136
      apps/client/src/locales/messages.nl.xlf
  30. 11
      libs/common/src/lib/dtos/create-asset-profile.dto.ts
  31. 4
      libs/common/src/lib/interfaces/portfolio-summary.interface.ts
  32. 7
      libs/common/src/lib/interfaces/responses/export-response.interface.ts
  33. 40
      libs/common/src/lib/personal-finance-tools.ts
  34. 4
      libs/ui/src/lib/assistant/assistant.html
  35. 677
      package-lock.json
  36. 26
      package.json
  37. 2
      test/import/ok/penthouse-apartment.json
  38. 4
      test/import/ok/sample.json

32
CHANGELOG.md

@ -5,6 +5,38 @@ 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).
## 2.247.0 - 2026-03-04
### Changed
- Upgraded `yahoo-finance2` from version `3.13.0` to `3.13.2`
## 2.246.0 - 2026-03-03
### Changed
- Removed the deprecated `committedFunds` from the summary of the portfolio details endpoint
- Upgraded `Nx` from version `22.4.5` to `22.5.3`
### Fixed
- Fixed an issue where the apply and reset filter buttons remained disabled in the assistant
## 2.245.0 - 2026-03-01
### Changed
- Excluded the scraper configuration from the import and export functionality
- Excluded the symbol mapping from the import and export functionality
- Improved the language localization for Dutch (`nl`)
- Improved the language localization for Italian (`it`)
- Improved the language localization for Spanish (`es`)
### Fixed
- Resolved the data source transformation in the errors of the performance endpoint
- Resolved the data source transformation in the export functionality
## 2.244.0 - 2026-02-28 ## 2.244.0 - 2026-02-28
### Changed ### Changed

6
README.md

@ -313,10 +313,12 @@ Ghostfolio is **100% free** and **open source**. We encourage and support an act
Not sure what to work on? We have [some ideas](https://github.com/ghostfolio/ghostfolio/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22%20no%3Aassignee), even for [newcomers](https://github.com/ghostfolio/ghostfolio/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22%20no%3Aassignee). Please join the Ghostfolio [Slack](https://join.slack.com/t/ghostfolio/shared_invite/zt-vsaan64h-F_I0fEo5M0P88lP9ibCxFg) channel or post to [@ghostfolio\_](https://x.com/ghostfolio_) on _X_. We would love to hear from you. Not sure what to work on? We have [some ideas](https://github.com/ghostfolio/ghostfolio/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22%20no%3Aassignee), even for [newcomers](https://github.com/ghostfolio/ghostfolio/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22%20no%3Aassignee). Please join the Ghostfolio [Slack](https://join.slack.com/t/ghostfolio/shared_invite/zt-vsaan64h-F_I0fEo5M0P88lP9ibCxFg) channel or post to [@ghostfolio\_](https://x.com/ghostfolio_) on _X_. We would love to hear from you.
If you like to support this project, become a [**Sponsor**](https://github.com/sponsors/ghostfolio), get [**Ghostfolio Premium**](https://ghostfol.io/en/pricing) or [**Buy me a coffee**](https://www.buymeacoffee.com/ghostfolio).
## Sponsors ## Sponsors
If you like to support this project, get [**Ghostfolio Premium**](https://ghostfol.io/en/pricing), become a [**Sponsor**](https://github.com/sponsors/ghostfolio) or [**Buy me a coffee**](https://www.buymeacoffee.com/ghostfolio).
<br />
<div align="center"> <div align="center">
<a href="https://www.testmuai.com?utm_medium=sponsor&utm_source=ghostfolio" target="_blank" title="TestMu AI - AI Powered Testing Tool"> <a href="https://www.testmuai.com?utm_medium=sponsor&utm_source=ghostfolio" target="_blank" title="TestMu AI - AI Powered Testing Tool">
<img alt="TestMu AI Logo" height="45" src="https://assets.testmuai.com/resources/images/logos/logo.svg" /> <img alt="TestMu AI Logo" height="45" src="https://assets.testmuai.com/resources/images/logos/logo.svg" />

5
apps/api/src/app/export/export.service.ts

@ -182,10 +182,8 @@ export class ExportService {
isActive, isActive,
isin, isin,
name, name,
scraperConfiguration,
sectors, sectors,
symbol, symbol,
symbolMapping,
url url
}) => { }) => {
return { return {
@ -204,11 +202,8 @@ export class ExportService {
isin, isin,
marketData: marketDataByAssetProfile[id], marketData: marketDataByAssetProfile[id],
name, name,
scraperConfiguration:
scraperConfiguration as unknown as Prisma.JsonArray,
sectors: sectors as unknown as Prisma.JsonArray, sectors: sectors as unknown as Prisma.JsonArray,
symbol, symbol,
symbolMapping,
url url
}; };
} }

1
apps/api/src/app/portfolio/portfolio.controller.ts

@ -187,7 +187,6 @@ export class PortfolioController {
portfolioSummary = nullifyValuesInObject(summary, [ portfolioSummary = nullifyValuesInObject(summary, [
'cash', 'cash',
'committedFunds',
'currentNetWorth', 'currentNetWorth',
'currentValueInBaseCurrency', 'currentValueInBaseCurrency',
'dividendInBaseCurrency', 'dividendInBaseCurrency',

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

@ -1914,8 +1914,6 @@ export class PortfolioService {
.plus(emergencyFundHoldingsValueInBaseCurrency) .plus(emergencyFundHoldingsValueInBaseCurrency)
.toNumber(); .toNumber();
const committedFunds = new Big(totalBuy).minus(totalSell);
const totalOfExcludedActivities = this.getSumOfActivityType({ const totalOfExcludedActivities = this.getSumOfActivityType({
userCurrency, userCurrency,
activities: excludedActivities, activities: excludedActivities,
@ -1979,7 +1977,6 @@ export class PortfolioService {
activityCount: activities.filter(({ type }) => { activityCount: activities.filter(({ type }) => {
return ['BUY', 'SELL'].includes(type); return ['BUY', 'SELL'].includes(type);
}).length, }).length,
committedFunds: committedFunds.toNumber(),
currentValueInBaseCurrency: currentValueInBaseCurrency.toNumber(), currentValueInBaseCurrency: currentValueInBaseCurrency.toNumber(),
dividendInBaseCurrency: dividendInBaseCurrency.toNumber(), dividendInBaseCurrency: dividendInBaseCurrency.toNumber(),
emergencyFund: { emergencyFund: {

2
apps/api/src/helper/object.helper.spec.ts

@ -1540,7 +1540,6 @@ describe('redactAttributes', () => {
netPerformanceWithCurrencyEffect: null, netPerformanceWithCurrencyEffect: null,
totalBuy: null, totalBuy: null,
totalSell: null, totalSell: null,
committedFunds: null,
currentValueInBaseCurrency: null, currentValueInBaseCurrency: null,
dividendInBaseCurrency: null, dividendInBaseCurrency: null,
emergencyFund: null, emergencyFund: null,
@ -3017,7 +3016,6 @@ describe('redactAttributes', () => {
netPerformanceWithCurrencyEffect: null, netPerformanceWithCurrencyEffect: null,
totalBuy: null, totalBuy: null,
totalSell: null, totalSell: null,
committedFunds: null,
currentValueInBaseCurrency: null, currentValueInBaseCurrency: null,
dividendInBaseCurrency: null, dividendInBaseCurrency: null,
emergencyFund: null, emergencyFund: null,

2
apps/api/src/interceptors/transform-data-source-in-response/transform-data-source-in-response.interceptor.ts

@ -62,8 +62,10 @@ export class TransformDataSourceInResponseInterceptor<
valueMap, valueMap,
object: data, object: data,
paths: [ paths: [
'activities[*].dataSource',
'activities[*].SymbolProfile.dataSource', 'activities[*].SymbolProfile.dataSource',
'benchmarks[*].dataSource', 'benchmarks[*].dataSource',
'errors[*].dataSource',
'fearAndGreedIndex.CRYPTOCURRENCIES.dataSource', 'fearAndGreedIndex.CRYPTOCURRENCIES.dataSource',
'fearAndGreedIndex.STOCKS.dataSource', 'fearAndGreedIndex.STOCKS.dataSource',
'holdings[*].dataSource', 'holdings[*].dataSource',

17
apps/client/src/app/pages/about/overview/about-overview-page.component.ts

@ -9,9 +9,10 @@ import {
ChangeDetectorRef, ChangeDetectorRef,
Component, Component,
CUSTOM_ELEMENTS_SCHEMA, CUSTOM_ELEMENTS_SCHEMA,
OnDestroy, DestroyRef,
OnInit OnInit
} from '@angular/core'; } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
import { IonIcon } from '@ionic/angular/standalone'; import { IonIcon } from '@ionic/angular/standalone';
@ -23,8 +24,6 @@ import {
logoX, logoX,
mail mail
} from 'ionicons/icons'; } from 'ionicons/icons';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
@Component({ @Component({
imports: [CommonModule, IonIcon, MatButtonModule, RouterModule], imports: [CommonModule, IonIcon, MatButtonModule, RouterModule],
@ -33,7 +32,7 @@ import { takeUntil } from 'rxjs/operators';
styleUrls: ['./about-overview-page.scss'], styleUrls: ['./about-overview-page.scss'],
templateUrl: './about-overview-page.html' templateUrl: './about-overview-page.html'
}) })
export class GfAboutOverviewPageComponent implements OnDestroy, OnInit { export class GfAboutOverviewPageComponent implements OnInit {
public hasPermissionForStatistics: boolean; public hasPermissionForStatistics: boolean;
public hasPermissionForSubscription: boolean; public hasPermissionForSubscription: boolean;
public isLoggedIn: boolean; public isLoggedIn: boolean;
@ -43,11 +42,10 @@ export class GfAboutOverviewPageComponent implements OnDestroy, OnInit {
public routerLinkOpenStartup = publicRoutes.openStartup.routerLink; public routerLinkOpenStartup = publicRoutes.openStartup.routerLink;
public user: User; public user: User;
private unsubscribeSubject = new Subject<void>();
public constructor( public constructor(
private changeDetectorRef: ChangeDetectorRef, private changeDetectorRef: ChangeDetectorRef,
private dataService: DataService, private dataService: DataService,
private destroyRef: DestroyRef,
private userService: UserService private userService: UserService
) { ) {
const { globalPermissions } = this.dataService.fetchInfo(); const { globalPermissions } = this.dataService.fetchInfo();
@ -67,7 +65,7 @@ export class GfAboutOverviewPageComponent implements OnDestroy, OnInit {
public ngOnInit() { public ngOnInit() {
this.userService.stateChanged this.userService.stateChanged
.pipe(takeUntil(this.unsubscribeSubject)) .pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((state) => { .subscribe((state) => {
if (state?.user) { if (state?.user) {
this.user = state.user; this.user = state.user;
@ -76,9 +74,4 @@ export class GfAboutOverviewPageComponent implements OnDestroy, OnInit {
} }
}); });
} }
public ngOnDestroy() {
this.unsubscribeSubject.next();
this.unsubscribeSubject.complete();
}
} }

12
apps/client/src/app/pages/about/privacy-policy/privacy-policy-page.component.ts

@ -1,6 +1,5 @@
import { Component, OnDestroy } from '@angular/core'; import { Component } from '@angular/core';
import { MarkdownModule } from 'ngx-markdown'; import { MarkdownModule } from 'ngx-markdown';
import { Subject } from 'rxjs';
@Component({ @Component({
imports: [MarkdownModule], imports: [MarkdownModule],
@ -8,11 +7,4 @@ import { Subject } from 'rxjs';
styleUrls: ['./privacy-policy-page.scss'], styleUrls: ['./privacy-policy-page.scss'],
templateUrl: './privacy-policy-page.html' templateUrl: './privacy-policy-page.html'
}) })
export class GfPrivacyPolicyPageComponent implements OnDestroy { export class GfPrivacyPolicyPageComponent {}
private unsubscribeSubject = new Subject<void>();
public ngOnDestroy() {
this.unsubscribeSubject.next();
this.unsubscribeSubject.complete();
}
}

12
apps/client/src/app/pages/about/terms-of-service/terms-of-service-page.component.ts

@ -1,6 +1,5 @@
import { Component, OnDestroy } from '@angular/core'; import { Component } from '@angular/core';
import { MarkdownModule } from 'ngx-markdown'; import { MarkdownModule } from 'ngx-markdown';
import { Subject } from 'rxjs';
@Component({ @Component({
imports: [MarkdownModule], imports: [MarkdownModule],
@ -8,11 +7,4 @@ import { Subject } from 'rxjs';
styleUrls: ['./terms-of-service-page.scss'], styleUrls: ['./terms-of-service-page.scss'],
templateUrl: './terms-of-service-page.html' templateUrl: './terms-of-service-page.html'
}) })
export class GfTermsOfServicePageComponent implements OnDestroy { export class GfTermsOfServicePageComponent {}
private unsubscribeSubject = new Subject<void>();
public ngOnDestroy() {
this.unsubscribeSubject.next();
this.unsubscribeSubject.complete();
}
}

18
apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.component.ts

@ -5,12 +5,7 @@ import { GfEntityLogoComponent } from '@ghostfolio/ui/entity-logo';
import { DataService } from '@ghostfolio/ui/services'; import { DataService } from '@ghostfolio/ui/services';
import { CommonModule, NgClass } from '@angular/common'; import { CommonModule, NgClass } from '@angular/common';
import { import { ChangeDetectionStrategy, Component, Inject } from '@angular/core';
ChangeDetectionStrategy,
Component,
Inject,
OnDestroy
} from '@angular/core';
import { import {
AbstractControl, AbstractControl,
FormBuilder, FormBuilder,
@ -30,7 +25,7 @@ import {
import { MatFormFieldModule } from '@angular/material/form-field'; import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input'; import { MatInputModule } from '@angular/material/input';
import { Platform } from '@prisma/client'; import { Platform } from '@prisma/client';
import { Observable, Subject } from 'rxjs'; import { Observable } from 'rxjs';
import { map, startWith } from 'rxjs/operators'; import { map, startWith } from 'rxjs/operators';
import { CreateOrUpdateAccountDialogParams } from './interfaces/interfaces'; import { CreateOrUpdateAccountDialogParams } from './interfaces/interfaces';
@ -55,14 +50,12 @@ import { CreateOrUpdateAccountDialogParams } from './interfaces/interfaces';
styleUrls: ['./create-or-update-account-dialog.scss'], styleUrls: ['./create-or-update-account-dialog.scss'],
templateUrl: 'create-or-update-account-dialog.html' templateUrl: 'create-or-update-account-dialog.html'
}) })
export class GfCreateOrUpdateAccountDialogComponent implements OnDestroy { export class GfCreateOrUpdateAccountDialogComponent {
public accountForm: FormGroup; public accountForm: FormGroup;
public currencies: string[] = []; public currencies: string[] = [];
public filteredPlatforms: Observable<Platform[]>; public filteredPlatforms: Observable<Platform[]>;
public platforms: Platform[] = []; public platforms: Platform[] = [];
private unsubscribeSubject = new Subject<void>();
public constructor( public constructor(
@Inject(MAT_DIALOG_DATA) public data: CreateOrUpdateAccountDialogParams, @Inject(MAT_DIALOG_DATA) public data: CreateOrUpdateAccountDialogParams,
private dataService: DataService, private dataService: DataService,
@ -170,11 +163,6 @@ export class GfCreateOrUpdateAccountDialogComponent implements OnDestroy {
} }
} }
public ngOnDestroy() {
this.unsubscribeSubject.next();
this.unsubscribeSubject.complete();
}
private autocompleteObjectValidator(): ValidatorFn { private autocompleteObjectValidator(): ValidatorFn {
return (control: AbstractControl) => { return (control: AbstractControl) => {
if (control.value && typeof control.value === 'string') { if (control.value && typeof control.value === 'string') {

17
apps/client/src/app/pages/accounts/transfer-balance/transfer-balance-dialog.component.ts

@ -1,12 +1,7 @@
import { TransferBalanceDto } from '@ghostfolio/common/dtos'; import { TransferBalanceDto } from '@ghostfolio/common/dtos';
import { GfEntityLogoComponent } from '@ghostfolio/ui/entity-logo'; import { GfEntityLogoComponent } from '@ghostfolio/ui/entity-logo';
import { import { ChangeDetectionStrategy, Component, Inject } from '@angular/core';
ChangeDetectionStrategy,
Component,
Inject,
OnDestroy
} from '@angular/core';
import { import {
AbstractControl, AbstractControl,
FormBuilder, FormBuilder,
@ -25,7 +20,6 @@ import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input'; import { MatInputModule } from '@angular/material/input';
import { MatSelectModule } from '@angular/material/select'; import { MatSelectModule } from '@angular/material/select';
import { Account } from '@prisma/client'; import { Account } from '@prisma/client';
import { Subject } from 'rxjs';
import { TransferBalanceDialogParams } from './interfaces/interfaces'; import { TransferBalanceDialogParams } from './interfaces/interfaces';
@ -45,13 +39,11 @@ import { TransferBalanceDialogParams } from './interfaces/interfaces';
styleUrls: ['./transfer-balance-dialog.scss'], styleUrls: ['./transfer-balance-dialog.scss'],
templateUrl: 'transfer-balance-dialog.html' templateUrl: 'transfer-balance-dialog.html'
}) })
export class GfTransferBalanceDialogComponent implements OnDestroy { export class GfTransferBalanceDialogComponent {
public accounts: Account[] = []; public accounts: Account[] = [];
public currency: string; public currency: string;
public transferBalanceForm: FormGroup; public transferBalanceForm: FormGroup;
private unsubscribeSubject = new Subject<void>();
public constructor( public constructor(
@Inject(MAT_DIALOG_DATA) public data: TransferBalanceDialogParams, @Inject(MAT_DIALOG_DATA) public data: TransferBalanceDialogParams,
public dialogRef: MatDialogRef<GfTransferBalanceDialogComponent>, public dialogRef: MatDialogRef<GfTransferBalanceDialogComponent>,
@ -93,11 +85,6 @@ export class GfTransferBalanceDialogComponent implements OnDestroy {
this.dialogRef.close({ account }); this.dialogRef.close({ account });
} }
public ngOnDestroy() {
this.unsubscribeSubject.next();
this.unsubscribeSubject.complete();
}
private compareAccounts(control: AbstractControl): ValidationErrors { private compareAccounts(control: AbstractControl): ValidationErrors {
const accountFrom = control.get('fromAccount'); const accountFrom = control.get('fromAccount');
const accountTo = control.get('toAccount'); const accountTo = control.get('toAccount');

12
apps/client/src/app/pages/blog/blog-page.component.ts

@ -1,13 +1,12 @@
import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import { DataService } from '@ghostfolio/ui/services'; import { DataService } from '@ghostfolio/ui/services';
import { Component, CUSTOM_ELEMENTS_SCHEMA, OnDestroy } from '@angular/core'; import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { MatCardModule } from '@angular/material/card'; import { MatCardModule } from '@angular/material/card';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
import { IonIcon } from '@ionic/angular/standalone'; import { IonIcon } from '@ionic/angular/standalone';
import { addIcons } from 'ionicons'; import { addIcons } from 'ionicons';
import { chevronForwardOutline } from 'ionicons/icons'; import { chevronForwardOutline } from 'ionicons/icons';
import { Subject } from 'rxjs';
@Component({ @Component({
host: { class: 'page' }, host: { class: 'page' },
@ -17,11 +16,9 @@ import { Subject } from 'rxjs';
styleUrls: ['./blog-page.scss'], styleUrls: ['./blog-page.scss'],
templateUrl: './blog-page.html' templateUrl: './blog-page.html'
}) })
export class GfBlogPageComponent implements OnDestroy { export class GfBlogPageComponent {
public hasPermissionForSubscription: boolean; public hasPermissionForSubscription: boolean;
private unsubscribeSubject = new Subject<void>();
public constructor(private dataService: DataService) { public constructor(private dataService: DataService) {
const info = this.dataService.fetchInfo(); const info = this.dataService.fetchInfo();
@ -32,9 +29,4 @@ export class GfBlogPageComponent implements OnDestroy {
addIcons({ chevronForwardOutline }); addIcons({ chevronForwardOutline });
} }
public ngOnDestroy() {
this.unsubscribeSubject.next();
this.unsubscribeSubject.complete();
}
} }

17
apps/client/src/app/pages/faq/faq-page.component.ts

@ -3,19 +3,13 @@ import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import { publicRoutes } from '@ghostfolio/common/routes/routes'; import { publicRoutes } from '@ghostfolio/common/routes/routes';
import { DataService } from '@ghostfolio/ui/services'; import { DataService } from '@ghostfolio/ui/services';
import { import { CUSTOM_ELEMENTS_SCHEMA, Component, OnInit } from '@angular/core';
CUSTOM_ELEMENTS_SCHEMA,
Component,
OnDestroy,
OnInit
} from '@angular/core';
import { MatTabsModule } from '@angular/material/tabs'; import { MatTabsModule } from '@angular/material/tabs';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
import { IonIcon } from '@ionic/angular/standalone'; import { IonIcon } from '@ionic/angular/standalone';
import { addIcons } from 'ionicons'; import { addIcons } from 'ionicons';
import { cloudyOutline, readerOutline, serverOutline } from 'ionicons/icons'; import { cloudyOutline, readerOutline, serverOutline } from 'ionicons/icons';
import { DeviceDetectorService } from 'ngx-device-detector'; import { DeviceDetectorService } from 'ngx-device-detector';
import { Subject } from 'rxjs';
@Component({ @Component({
host: { class: 'page has-tabs' }, host: { class: 'page has-tabs' },
@ -25,13 +19,11 @@ import { Subject } from 'rxjs';
styleUrls: ['./faq-page.scss'], styleUrls: ['./faq-page.scss'],
templateUrl: './faq-page.html' templateUrl: './faq-page.html'
}) })
export class GfFaqPageComponent implements OnDestroy, OnInit { export class GfFaqPageComponent implements OnInit {
public deviceType: string; public deviceType: string;
public hasPermissionForSubscription: boolean; public hasPermissionForSubscription: boolean;
public tabs: TabConfiguration[] = []; public tabs: TabConfiguration[] = [];
private unsubscribeSubject = new Subject<void>();
public constructor( public constructor(
private dataService: DataService, private dataService: DataService,
private deviceService: DeviceDetectorService private deviceService: DeviceDetectorService
@ -68,9 +60,4 @@ export class GfFaqPageComponent implements OnDestroy, OnInit {
public ngOnInit() { public ngOnInit() {
this.deviceType = this.deviceService.getDeviceInfo().deviceType; this.deviceType = this.deviceService.getDeviceInfo().deviceType;
} }
public ngOnDestroy() {
this.unsubscribeSubject.next();
this.unsubscribeSubject.complete();
}
} }

12
apps/client/src/app/pages/faq/self-hosting/self-hosting-page.component.ts

@ -1,10 +1,9 @@
import { publicRoutes } from '@ghostfolio/common/routes/routes'; import { publicRoutes } from '@ghostfolio/common/routes/routes';
import { GfPremiumIndicatorComponent } from '@ghostfolio/ui/premium-indicator'; import { GfPremiumIndicatorComponent } from '@ghostfolio/ui/premium-indicator';
import { CUSTOM_ELEMENTS_SCHEMA, Component, OnDestroy } from '@angular/core'; import { CUSTOM_ELEMENTS_SCHEMA, Component } from '@angular/core';
import { MatCardModule } from '@angular/material/card'; import { MatCardModule } from '@angular/material/card';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
import { Subject } from 'rxjs';
@Component({ @Component({
host: { class: 'page' }, host: { class: 'page' },
@ -14,13 +13,6 @@ import { Subject } from 'rxjs';
styleUrls: ['./self-hosting-page.scss'], styleUrls: ['./self-hosting-page.scss'],
templateUrl: './self-hosting-page.html' templateUrl: './self-hosting-page.html'
}) })
export class GfSelfHostingPageComponent implements OnDestroy { export class GfSelfHostingPageComponent {
public pricingUrl = `https://ghostfol.io/${document.documentElement.lang}/${publicRoutes.pricing.path}`; public pricingUrl = `https://ghostfol.io/${document.documentElement.lang}/${publicRoutes.pricing.path}`;
private unsubscribeSubject = new Subject<void>();
public ngOnDestroy() {
this.unsubscribeSubject.next();
this.unsubscribeSubject.complete();
}
} }

16
apps/client/src/app/pages/features/features-page.component.ts

@ -5,11 +5,11 @@ import { publicRoutes } from '@ghostfolio/common/routes/routes';
import { GfPremiumIndicatorComponent } from '@ghostfolio/ui/premium-indicator'; import { GfPremiumIndicatorComponent } from '@ghostfolio/ui/premium-indicator';
import { DataService } from '@ghostfolio/ui/services'; import { DataService } from '@ghostfolio/ui/services';
import { ChangeDetectorRef, Component, OnDestroy } from '@angular/core'; import { ChangeDetectorRef, Component, DestroyRef } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card'; import { MatCardModule } from '@angular/material/card';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
import { Subject, takeUntil } from 'rxjs';
@Component({ @Component({
host: { class: 'page' }, host: { class: 'page' },
@ -23,7 +23,7 @@ import { Subject, takeUntil } from 'rxjs';
styleUrls: ['./features-page.scss'], styleUrls: ['./features-page.scss'],
templateUrl: './features-page.html' templateUrl: './features-page.html'
}) })
export class GfFeaturesPageComponent implements OnDestroy { export class GfFeaturesPageComponent {
public hasPermissionForSubscription: boolean; public hasPermissionForSubscription: boolean;
public hasPermissionToCreateUser: boolean; public hasPermissionToCreateUser: boolean;
public info: InfoItem; public info: InfoItem;
@ -31,11 +31,10 @@ export class GfFeaturesPageComponent implements OnDestroy {
public routerLinkResources = publicRoutes.resources.routerLink; public routerLinkResources = publicRoutes.resources.routerLink;
public user: User; public user: User;
private unsubscribeSubject = new Subject<void>();
public constructor( public constructor(
private changeDetectorRef: ChangeDetectorRef, private changeDetectorRef: ChangeDetectorRef,
private dataService: DataService, private dataService: DataService,
private destroyRef: DestroyRef,
private userService: UserService private userService: UserService
) { ) {
this.info = this.dataService.fetchInfo(); this.info = this.dataService.fetchInfo();
@ -43,7 +42,7 @@ export class GfFeaturesPageComponent implements OnDestroy {
public ngOnInit() { public ngOnInit() {
this.userService.stateChanged this.userService.stateChanged
.pipe(takeUntil(this.unsubscribeSubject)) .pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((state) => { .subscribe((state) => {
if (state?.user) { if (state?.user) {
this.user = state.user; this.user = state.user;
@ -62,9 +61,4 @@ export class GfFeaturesPageComponent implements OnDestroy {
permissions.createUserAccount permissions.createUserAccount
); );
} }
public ngOnDestroy() {
this.unsubscribeSubject.next();
this.unsubscribeSubject.complete();
}
} }

19
apps/client/src/app/pages/home/home-page.component.ts

@ -8,9 +8,10 @@ import {
ChangeDetectorRef, ChangeDetectorRef,
Component, Component,
CUSTOM_ELEMENTS_SCHEMA, CUSTOM_ELEMENTS_SCHEMA,
OnDestroy, DestroyRef,
OnInit OnInit
} from '@angular/core'; } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { MatTabsModule } from '@angular/material/tabs'; import { MatTabsModule } from '@angular/material/tabs';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
import { IonIcon } from '@ionic/angular/standalone'; import { IonIcon } from '@ionic/angular/standalone';
@ -23,8 +24,6 @@ import {
readerOutline readerOutline
} from 'ionicons/icons'; } from 'ionicons/icons';
import { DeviceDetectorService } from 'ngx-device-detector'; import { DeviceDetectorService } from 'ngx-device-detector';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
@Component({ @Component({
host: { class: 'page has-tabs' }, host: { class: 'page has-tabs' },
@ -34,22 +33,21 @@ import { takeUntil } from 'rxjs/operators';
styleUrls: ['./home-page.scss'], styleUrls: ['./home-page.scss'],
templateUrl: './home-page.html' templateUrl: './home-page.html'
}) })
export class GfHomePageComponent implements OnDestroy, OnInit { export class GfHomePageComponent implements OnInit {
public deviceType: string; public deviceType: string;
public hasImpersonationId: boolean; public hasImpersonationId: boolean;
public tabs: TabConfiguration[] = []; public tabs: TabConfiguration[] = [];
public user: User; public user: User;
private unsubscribeSubject = new Subject<void>();
public constructor( public constructor(
private changeDetectorRef: ChangeDetectorRef, private changeDetectorRef: ChangeDetectorRef,
private destroyRef: DestroyRef,
private deviceService: DeviceDetectorService, private deviceService: DeviceDetectorService,
private impersonationStorageService: ImpersonationStorageService, private impersonationStorageService: ImpersonationStorageService,
private userService: UserService private userService: UserService
) { ) {
this.userService.stateChanged this.userService.stateChanged
.pipe(takeUntil(this.unsubscribeSubject)) .pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((state) => { .subscribe((state) => {
if (state?.user) { if (state?.user) {
this.user = state.user; this.user = state.user;
@ -110,14 +108,9 @@ export class GfHomePageComponent implements OnDestroy, OnInit {
this.impersonationStorageService this.impersonationStorageService
.onChangeHasImpersonation() .onChangeHasImpersonation()
.pipe(takeUntil(this.unsubscribeSubject)) .pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((impersonationId) => { .subscribe((impersonationId) => {
this.hasImpersonationId = !!impersonationId; this.hasImpersonationId = !!impersonationId;
}); });
} }
public ngOnDestroy() {
this.unsubscribeSubject.next();
this.unsubscribeSubject.complete();
}
} }

10
apps/client/src/app/pages/i18n/i18n-page.component.ts

@ -1,5 +1,4 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { Subject } from 'rxjs';
@Component({ @Component({
host: { class: 'page' }, host: { class: 'page' },
@ -8,11 +7,4 @@ import { Subject } from 'rxjs';
styleUrls: ['./i18n-page.scss'], styleUrls: ['./i18n-page.scss'],
templateUrl: './i18n-page.html' templateUrl: './i18n-page.html'
}) })
export class GfI18nPageComponent { export class GfI18nPageComponent {}
private unsubscribeSubject = new Subject<void>();
public ngOnDestroy() {
this.unsubscribeSubject.next();
this.unsubscribeSubject.complete();
}
}

30
apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts

@ -22,7 +22,13 @@ import { GfValueComponent } from '@ghostfolio/ui/value';
import { GfWorldMapChartComponent } from '@ghostfolio/ui/world-map-chart'; import { GfWorldMapChartComponent } from '@ghostfolio/ui/world-map-chart';
import { NgClass } from '@angular/common'; import { NgClass } from '@angular/common';
import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; import {
ChangeDetectorRef,
Component,
DestroyRef,
OnInit
} from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { MatCardModule } from '@angular/material/card'; import { MatCardModule } from '@angular/material/card';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { MatProgressBarModule } from '@angular/material/progress-bar'; import { MatProgressBarModule } from '@angular/material/progress-bar';
@ -36,8 +42,6 @@ import {
} from '@prisma/client'; } from '@prisma/client';
import { isNumber } from 'lodash'; import { isNumber } from 'lodash';
import { DeviceDetectorService } from 'ngx-device-detector'; import { DeviceDetectorService } from 'ngx-device-detector';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
@Component({ @Component({
imports: [ imports: [
@ -54,7 +58,7 @@ import { takeUntil } from 'rxjs/operators';
styleUrls: ['./allocations-page.scss'], styleUrls: ['./allocations-page.scss'],
templateUrl: './allocations-page.html' templateUrl: './allocations-page.html'
}) })
export class GfAllocationsPageComponent implements OnDestroy, OnInit { export class GfAllocationsPageComponent implements OnInit {
public accounts: { public accounts: {
[id: string]: Pick<Account, 'name'> & { [id: string]: Pick<Account, 'name'> & {
id: string; id: string;
@ -119,11 +123,10 @@ export class GfAllocationsPageComponent implements OnDestroy, OnInit {
public user: User; public user: User;
public worldMapChartFormat: string; public worldMapChartFormat: string;
private unsubscribeSubject = new Subject<void>();
public constructor( public constructor(
private changeDetectorRef: ChangeDetectorRef, private changeDetectorRef: ChangeDetectorRef,
private dataService: DataService, private dataService: DataService,
private destroyRef: DestroyRef,
private deviceService: DeviceDetectorService, private deviceService: DeviceDetectorService,
private dialog: MatDialog, private dialog: MatDialog,
private impersonationStorageService: ImpersonationStorageService, private impersonationStorageService: ImpersonationStorageService,
@ -132,7 +135,7 @@ export class GfAllocationsPageComponent implements OnDestroy, OnInit {
private userService: UserService private userService: UserService
) { ) {
this.route.queryParams this.route.queryParams
.pipe(takeUntil(this.unsubscribeSubject)) .pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((params) => { .subscribe((params) => {
if (params['accountId'] && params['accountDetailDialog']) { if (params['accountId'] && params['accountDetailDialog']) {
this.openAccountDetailDialog(params['accountId']); this.openAccountDetailDialog(params['accountId']);
@ -145,13 +148,13 @@ export class GfAllocationsPageComponent implements OnDestroy, OnInit {
this.impersonationStorageService this.impersonationStorageService
.onChangeHasImpersonation() .onChangeHasImpersonation()
.pipe(takeUntil(this.unsubscribeSubject)) .pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((impersonationId) => { .subscribe((impersonationId) => {
this.hasImpersonationId = !!impersonationId; this.hasImpersonationId = !!impersonationId;
}); });
this.userService.stateChanged this.userService.stateChanged
.pipe(takeUntil(this.unsubscribeSubject)) .pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((state) => { .subscribe((state) => {
if (state?.user) { if (state?.user) {
this.user = state.user; this.user = state.user;
@ -166,7 +169,7 @@ export class GfAllocationsPageComponent implements OnDestroy, OnInit {
this.initialize(); this.initialize();
this.fetchPortfolioDetails() this.fetchPortfolioDetails()
.pipe(takeUntil(this.unsubscribeSubject)) .pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((portfolioDetails) => { .subscribe((portfolioDetails) => {
this.initialize(); this.initialize();
@ -202,11 +205,6 @@ export class GfAllocationsPageComponent implements OnDestroy, OnInit {
} }
} }
public ngOnDestroy() {
this.unsubscribeSubject.next();
this.unsubscribeSubject.complete();
}
private extractEtfProvider({ private extractEtfProvider({
assetSubClass, assetSubClass,
name name
@ -578,7 +576,7 @@ export class GfAllocationsPageComponent implements OnDestroy, OnInit {
dialogRef dialogRef
.afterClosed() .afterClosed()
.pipe(takeUntil(this.unsubscribeSubject)) .pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(() => { .subscribe(() => {
this.router.navigate(['.'], { relativeTo: this.route }); this.router.navigate(['.'], { relativeTo: this.route });
}); });

22
apps/client/src/app/pages/portfolio/portfolio-page.component.ts

@ -2,7 +2,13 @@ import { UserService } from '@ghostfolio/client/services/user/user.service';
import { TabConfiguration, User } from '@ghostfolio/common/interfaces'; import { TabConfiguration, User } from '@ghostfolio/common/interfaces';
import { internalRoutes } from '@ghostfolio/common/routes/routes'; import { internalRoutes } from '@ghostfolio/common/routes/routes';
import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; import {
ChangeDetectorRef,
Component,
DestroyRef,
OnInit
} from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { MatTabsModule } from '@angular/material/tabs'; import { MatTabsModule } from '@angular/material/tabs';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
import { addIcons } from 'ionicons'; import { addIcons } from 'ionicons';
@ -14,8 +20,6 @@ import {
swapVerticalOutline swapVerticalOutline
} from 'ionicons/icons'; } from 'ionicons/icons';
import { DeviceDetectorService } from 'ngx-device-detector'; import { DeviceDetectorService } from 'ngx-device-detector';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
@Component({ @Component({
host: { class: 'page has-tabs' }, host: { class: 'page has-tabs' },
@ -24,20 +28,19 @@ import { takeUntil } from 'rxjs/operators';
styleUrls: ['./portfolio-page.scss'], styleUrls: ['./portfolio-page.scss'],
templateUrl: './portfolio-page.html' templateUrl: './portfolio-page.html'
}) })
export class PortfolioPageComponent implements OnDestroy, OnInit { export class PortfolioPageComponent implements OnInit {
public deviceType: string; public deviceType: string;
public tabs: TabConfiguration[] = []; public tabs: TabConfiguration[] = [];
public user: User; public user: User;
private unsubscribeSubject = new Subject<void>();
public constructor( public constructor(
private changeDetectorRef: ChangeDetectorRef, private changeDetectorRef: ChangeDetectorRef,
private destroyRef: DestroyRef,
private deviceService: DeviceDetectorService, private deviceService: DeviceDetectorService,
private userService: UserService private userService: UserService
) { ) {
this.userService.stateChanged this.userService.stateChanged
.pipe(takeUntil(this.unsubscribeSubject)) .pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((state) => { .subscribe((state) => {
if (state?.user) { if (state?.user) {
this.tabs = [ this.tabs = [
@ -87,9 +90,4 @@ export class PortfolioPageComponent implements OnDestroy, OnInit {
public ngOnInit() { public ngOnInit() {
this.deviceType = this.deviceService.getDeviceInfo().deviceType; this.deviceType = this.deviceService.getDeviceInfo().deviceType;
} }
public ngOnDestroy() {
this.unsubscribeSubject.next();
this.unsubscribeSubject.complete();
}
} }

17
apps/client/src/app/pages/register/register-page.component.ts

@ -7,15 +7,14 @@ import { DataService } from '@ghostfolio/ui/services';
import { import {
Component, Component,
CUSTOM_ELEMENTS_SCHEMA, CUSTOM_ELEMENTS_SCHEMA,
OnDestroy, DestroyRef,
OnInit OnInit
} from '@angular/core'; } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { Router, RouterModule } from '@angular/router'; import { Router, RouterModule } from '@angular/router';
import { DeviceDetectorService } from 'ngx-device-detector'; import { DeviceDetectorService } from 'ngx-device-detector';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { UserAccountRegistrationDialogParams } from './user-account-registration-dialog/interfaces/interfaces'; import { UserAccountRegistrationDialogParams } from './user-account-registration-dialog/interfaces/interfaces';
import { GfUserAccountRegistrationDialogComponent } from './user-account-registration-dialog/user-account-registration-dialog.component'; import { GfUserAccountRegistrationDialogComponent } from './user-account-registration-dialog/user-account-registration-dialog.component';
@ -28,7 +27,7 @@ import { GfUserAccountRegistrationDialogComponent } from './user-account-registr
styleUrls: ['./register-page.scss'], styleUrls: ['./register-page.scss'],
templateUrl: './register-page.html' templateUrl: './register-page.html'
}) })
export class GfRegisterPageComponent implements OnDestroy, OnInit { export class GfRegisterPageComponent implements OnInit {
public deviceType: string; public deviceType: string;
public hasPermissionForAuthGoogle: boolean; public hasPermissionForAuthGoogle: boolean;
public hasPermissionForAuthToken: boolean; public hasPermissionForAuthToken: boolean;
@ -37,10 +36,9 @@ export class GfRegisterPageComponent implements OnDestroy, OnInit {
public historicalDataItems: LineChartItem[]; public historicalDataItems: LineChartItem[];
public info: InfoItem; public info: InfoItem;
private unsubscribeSubject = new Subject<void>();
public constructor( public constructor(
private dataService: DataService, private dataService: DataService,
private destroyRef: DestroyRef,
private deviceService: DeviceDetectorService, private deviceService: DeviceDetectorService,
private dialog: MatDialog, private dialog: MatDialog,
private router: Router, private router: Router,
@ -93,7 +91,7 @@ export class GfRegisterPageComponent implements OnDestroy, OnInit {
dialogRef dialogRef
.afterClosed() .afterClosed()
.pipe(takeUntil(this.unsubscribeSubject)) .pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((authToken) => { .subscribe((authToken) => {
if (authToken) { if (authToken) {
this.tokenStorageService.saveToken(authToken, true); this.tokenStorageService.saveToken(authToken, true);
@ -102,9 +100,4 @@ export class GfRegisterPageComponent implements OnDestroy, OnInit {
} }
}); });
} }
public ngOnDestroy() {
this.unsubscribeSubject.next();
this.unsubscribeSubject.complete();
}
} }

12
apps/client/src/app/pages/resources/personal-finance-tools/personal-finance-tools-page.component.ts

@ -1,13 +1,12 @@
import { personalFinanceTools } from '@ghostfolio/common/personal-finance-tools'; import { personalFinanceTools } from '@ghostfolio/common/personal-finance-tools';
import { publicRoutes } from '@ghostfolio/common/routes/routes'; import { publicRoutes } from '@ghostfolio/common/routes/routes';
import { Component, OnDestroy } from '@angular/core'; import { Component } from '@angular/core';
import { MatCardModule } from '@angular/material/card'; import { MatCardModule } from '@angular/material/card';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
import { IonIcon } from '@ionic/angular/standalone'; import { IonIcon } from '@ionic/angular/standalone';
import { addIcons } from 'ionicons'; import { addIcons } from 'ionicons';
import { chevronForwardOutline } from 'ionicons/icons'; import { chevronForwardOutline } from 'ionicons/icons';
import { Subject } from 'rxjs';
@Component({ @Component({
host: { class: 'page' }, host: { class: 'page' },
@ -16,7 +15,7 @@ import { Subject } from 'rxjs';
styleUrls: ['./personal-finance-tools-page.scss'], styleUrls: ['./personal-finance-tools-page.scss'],
templateUrl: './personal-finance-tools-page.html' templateUrl: './personal-finance-tools-page.html'
}) })
export class PersonalFinanceToolsPageComponent implements OnDestroy { export class PersonalFinanceToolsPageComponent {
public pathAlternativeTo = public pathAlternativeTo =
publicRoutes.resources.subRoutes.personalFinanceTools.subRoutes.product publicRoutes.resources.subRoutes.personalFinanceTools.subRoutes.product
.path + '-'; .path + '-';
@ -28,14 +27,7 @@ export class PersonalFinanceToolsPageComponent implements OnDestroy {
}); });
public routerLinkAbout = publicRoutes.about.routerLink; public routerLinkAbout = publicRoutes.about.routerLink;
private unsubscribeSubject = new Subject<void>();
public constructor() { public constructor() {
addIcons({ chevronForwardOutline }); addIcons({ chevronForwardOutline });
} }
public ngOnDestroy() {
this.unsubscribeSubject.next();
this.unsubscribeSubject.complete();
}
} }

16
apps/client/src/app/pages/user-account/user-account-page.component.ts

@ -6,16 +6,16 @@ import {
ChangeDetectorRef, ChangeDetectorRef,
Component, Component,
CUSTOM_ELEMENTS_SCHEMA, CUSTOM_ELEMENTS_SCHEMA,
OnDestroy, DestroyRef,
OnInit OnInit
} from '@angular/core'; } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { MatTabsModule } from '@angular/material/tabs'; import { MatTabsModule } from '@angular/material/tabs';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
import { IonIcon } from '@ionic/angular/standalone'; import { IonIcon } from '@ionic/angular/standalone';
import { addIcons } from 'ionicons'; import { addIcons } from 'ionicons';
import { diamondOutline, keyOutline, settingsOutline } from 'ionicons/icons'; import { diamondOutline, keyOutline, settingsOutline } from 'ionicons/icons';
import { DeviceDetectorService } from 'ngx-device-detector'; import { DeviceDetectorService } from 'ngx-device-detector';
import { Subject, takeUntil } from 'rxjs';
@Component({ @Component({
host: { class: 'page has-tabs' }, host: { class: 'page has-tabs' },
@ -25,20 +25,19 @@ import { Subject, takeUntil } from 'rxjs';
styleUrls: ['./user-account-page.scss'], styleUrls: ['./user-account-page.scss'],
templateUrl: './user-account-page.html' templateUrl: './user-account-page.html'
}) })
export class GfUserAccountPageComponent implements OnDestroy, OnInit { export class GfUserAccountPageComponent implements OnInit {
public deviceType: string; public deviceType: string;
public tabs: TabConfiguration[] = []; public tabs: TabConfiguration[] = [];
public user: User; public user: User;
private unsubscribeSubject = new Subject<void>();
public constructor( public constructor(
private changeDetectorRef: ChangeDetectorRef, private changeDetectorRef: ChangeDetectorRef,
private destroyRef: DestroyRef,
private deviceService: DeviceDetectorService, private deviceService: DeviceDetectorService,
private userService: UserService private userService: UserService
) { ) {
this.userService.stateChanged this.userService.stateChanged
.pipe(takeUntil(this.unsubscribeSubject)) .pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((state) => { .subscribe((state) => {
if (state?.user) { if (state?.user) {
this.user = state.user; this.user = state.user;
@ -73,9 +72,4 @@ export class GfUserAccountPageComponent implements OnDestroy, OnInit {
public ngOnInit() { public ngOnInit() {
this.deviceType = this.deviceService.getDeviceInfo().deviceType; this.deviceType = this.deviceService.getDeviceInfo().deviceType;
} }
public ngOnDestroy() {
this.unsubscribeSubject.next();
this.unsubscribeSubject.complete();
}
} }

24
apps/client/src/app/pages/webauthn/webauthn-page.component.ts

@ -2,12 +2,16 @@ import { TokenStorageService } from '@ghostfolio/client/services/token-storage.s
import { WebAuthnService } from '@ghostfolio/client/services/web-authn.service'; import { WebAuthnService } from '@ghostfolio/client/services/web-authn.service';
import { GfLogoComponent } from '@ghostfolio/ui/logo'; import { GfLogoComponent } from '@ghostfolio/ui/logo';
import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; import {
ChangeDetectorRef,
Component,
DestroyRef,
OnInit
} from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
@Component({ @Component({
host: { class: 'page' }, host: { class: 'page' },
@ -16,13 +20,12 @@ import { takeUntil } from 'rxjs/operators';
styleUrls: ['./webauthn-page.scss'], styleUrls: ['./webauthn-page.scss'],
templateUrl: './webauthn-page.html' templateUrl: './webauthn-page.html'
}) })
export class GfWebauthnPageComponent implements OnDestroy, OnInit { export class GfWebauthnPageComponent implements OnInit {
public hasError = false; public hasError = false;
private unsubscribeSubject = new Subject<void>();
public constructor( public constructor(
private changeDetectorRef: ChangeDetectorRef, private changeDetectorRef: ChangeDetectorRef,
private destroyRef: DestroyRef,
private router: Router, private router: Router,
private tokenStorageService: TokenStorageService, private tokenStorageService: TokenStorageService,
private webAuthnService: WebAuthnService private webAuthnService: WebAuthnService
@ -35,7 +38,7 @@ export class GfWebauthnPageComponent implements OnDestroy, OnInit {
public deregisterDevice() { public deregisterDevice() {
this.webAuthnService this.webAuthnService
.deregister() .deregister()
.pipe(takeUntil(this.unsubscribeSubject)) .pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(() => { .subscribe(() => {
this.router.navigate(['/']); this.router.navigate(['/']);
}); });
@ -46,7 +49,7 @@ export class GfWebauthnPageComponent implements OnDestroy, OnInit {
this.webAuthnService this.webAuthnService
.login() .login()
.pipe(takeUntil(this.unsubscribeSubject)) .pipe(takeUntilDestroyed(this.destroyRef))
.subscribe( .subscribe(
({ authToken }) => { ({ authToken }) => {
this.tokenStorageService.saveToken(authToken, false); this.tokenStorageService.saveToken(authToken, false);
@ -59,9 +62,4 @@ export class GfWebauthnPageComponent implements OnDestroy, OnInit {
} }
); );
} }
public ngOnDestroy() {
this.unsubscribeSubject.next();
this.unsubscribeSubject.complete();
}
} }

22
apps/client/src/app/pages/zen/zen-page.component.ts

@ -2,15 +2,19 @@ import { UserService } from '@ghostfolio/client/services/user/user.service';
import { TabConfiguration, User } from '@ghostfolio/common/interfaces'; import { TabConfiguration, User } from '@ghostfolio/common/interfaces';
import { internalRoutes } from '@ghostfolio/common/routes/routes'; import { internalRoutes } from '@ghostfolio/common/routes/routes';
import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; import {
ChangeDetectorRef,
Component,
DestroyRef,
OnInit
} from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { MatTabsModule } from '@angular/material/tabs'; import { MatTabsModule } from '@angular/material/tabs';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
import { IonIcon } from '@ionic/angular/standalone'; import { IonIcon } from '@ionic/angular/standalone';
import { addIcons } from 'ionicons'; import { addIcons } from 'ionicons';
import { albumsOutline, analyticsOutline } from 'ionicons/icons'; import { albumsOutline, analyticsOutline } from 'ionicons/icons';
import { DeviceDetectorService } from 'ngx-device-detector'; import { DeviceDetectorService } from 'ngx-device-detector';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
@Component({ @Component({
host: { class: 'page has-tabs' }, host: { class: 'page has-tabs' },
@ -19,20 +23,19 @@ import { takeUntil } from 'rxjs/operators';
styleUrls: ['./zen-page.scss'], styleUrls: ['./zen-page.scss'],
templateUrl: './zen-page.html' templateUrl: './zen-page.html'
}) })
export class GfZenPageComponent implements OnDestroy, OnInit { export class GfZenPageComponent implements OnInit {
public deviceType: string; public deviceType: string;
public tabs: TabConfiguration[] = []; public tabs: TabConfiguration[] = [];
public user: User; public user: User;
private unsubscribeSubject = new Subject<void>();
public constructor( public constructor(
private changeDetectorRef: ChangeDetectorRef, private changeDetectorRef: ChangeDetectorRef,
private destroyRef: DestroyRef,
private deviceService: DeviceDetectorService, private deviceService: DeviceDetectorService,
private userService: UserService private userService: UserService
) { ) {
this.userService.stateChanged this.userService.stateChanged
.pipe(takeUntil(this.unsubscribeSubject)) .pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((state) => { .subscribe((state) => {
if (state?.user) { if (state?.user) {
this.tabs = [ this.tabs = [
@ -59,9 +62,4 @@ export class GfZenPageComponent implements OnDestroy, OnInit {
public ngOnInit() { public ngOnInit() {
this.deviceType = this.deviceService.getDeviceInfo().deviceType; this.deviceType = this.deviceService.getDeviceInfo().deviceType;
} }
public ngOnDestroy() {
this.unsubscribeSubject.next();
this.unsubscribeSubject.complete();
}
} }

2
apps/client/src/app/services/import-activities.service.ts

@ -97,9 +97,7 @@ export class ImportActivitiesService {
isin: null, isin: null,
marketData: [], marketData: [],
name: symbol, name: symbol,
scraperConfiguration: null,
sectors: [], sectors: [],
symbolMapping: {},
url: null url: null
}); });
} }

2
apps/client/src/locales/messages.es.xlf

@ -3344,7 +3344,7 @@
</trans-unit> </trans-unit>
<trans-unit id="9218541487912911620" datatype="html"> <trans-unit id="9218541487912911620" datatype="html">
<source>No Activities</source> <source>No Activities</source>
<target state="new">No Activities</target> <target state="translated">Sin actividades</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/admin-market-data.component.ts</context> <context context-type="sourcefile">apps/client/src/app/components/admin-market-data/admin-market-data.component.ts</context>
<context context-type="linenumber">146</context> <context context-type="linenumber">146</context>

2
apps/client/src/locales/messages.it.xlf

@ -3244,7 +3244,7 @@
</trans-unit> </trans-unit>
<trans-unit id="4739818603756173797" datatype="html"> <trans-unit id="4739818603756173797" datatype="html">
<source>Summary</source> <source>Summary</source>
<target state="translated">Summario</target> <target state="translated">Riepilogo</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/home-summary/home-summary.html</context> <context context-type="sourcefile">apps/client/src/app/components/home-summary/home-summary.html</context>
<context context-type="linenumber">2</context> <context context-type="linenumber">2</context>

136
apps/client/src/locales/messages.nl.xlf

@ -39,7 +39,7 @@
</trans-unit> </trans-unit>
<trans-unit id="9153520284278555926" datatype="html"> <trans-unit id="9153520284278555926" datatype="html">
<source>please</source> <source>please</source>
<target state="new">please</target> <target state="translated">alsjeblieft</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context>
<context context-type="linenumber">333</context> <context context-type="linenumber">333</context>
@ -83,7 +83,7 @@
</trans-unit> </trans-unit>
<trans-unit id="1351814922314683865" datatype="html"> <trans-unit id="1351814922314683865" datatype="html">
<source>with</source> <source>with</source>
<target state="new">with</target> <target state="translated">met</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/subscription-interstitial-dialog/subscription-interstitial-dialog.html</context> <context context-type="sourcefile">apps/client/src/app/components/subscription-interstitial-dialog/subscription-interstitial-dialog.html</context>
<context context-type="linenumber">87</context> <context context-type="linenumber">87</context>
@ -371,7 +371,7 @@
</trans-unit> </trans-unit>
<trans-unit id="5611965261696422586" datatype="html"> <trans-unit id="5611965261696422586" datatype="html">
<source>and is driven by the efforts of its <x id="START_LINK" ctype="x-a" equiv-text="&lt;a href=&quot;https://github.com/ghostfolio/ghostfolio/graphs/contributors&quot; title=&quot;Contributors to Ghostfolio&quot; &gt;"/>contributors<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/></source> <source>and is driven by the efforts of its <x id="START_LINK" ctype="x-a" equiv-text="&lt;a href=&quot;https://github.com/ghostfolio/ghostfolio/graphs/contributors&quot; title=&quot;Contributors to Ghostfolio&quot; &gt;"/>contributors<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/></source>
<target state="new">and is driven by the efforts of its <x id="START_LINK" ctype="x-a" equiv-text="&lt;a href=&quot;https://github.com/ghostfolio/ghostfolio/graphs/contributors&quot; title=&quot;Contributors to Ghostfolio&quot; &gt;"/>contributors<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/></target> <target state="translated">en wordt gedreven door de inspanningen van zijn <x id="START_LINK" ctype="x-a" equiv-text="&lt;a href=&quot;https://github.com/ghostfolio/ghostfolio/graphs/contributors&quot; title=&quot;Contributors to Ghostfolio&quot; &gt;"/>bijdragers<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/></target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context>
<context context-type="linenumber">49</context> <context context-type="linenumber">49</context>
@ -655,7 +655,7 @@
</trans-unit> </trans-unit>
<trans-unit id="2395205455607568422" datatype="html"> <trans-unit id="2395205455607568422" datatype="html">
<source>No auto-renewal on membership.</source> <source>No auto-renewal on membership.</source>
<target state="new">No auto-renewal on membership.</target> <target state="translated">Het lidmaatschap wordt niet automatisch verlengd.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/user-account-membership/user-account-membership.html</context> <context context-type="sourcefile">apps/client/src/app/components/user-account-membership/user-account-membership.html</context>
<context context-type="linenumber">74</context> <context context-type="linenumber">74</context>
@ -1099,7 +1099,7 @@
</trans-unit> </trans-unit>
<trans-unit id="366169681580494481" datatype="html"> <trans-unit id="366169681580494481" datatype="html">
<source>Performance with currency effect</source> <source>Performance with currency effect</source>
<target state="new">Performance with currency effect</target> <target state="translated">Prestaties met valuta-effect</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/analysis/analysis-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/portfolio/analysis/analysis-page.html</context>
<context context-type="linenumber">135</context> <context context-type="linenumber">135</context>
@ -1915,7 +1915,7 @@
</trans-unit> </trans-unit>
<trans-unit id="6004588582437169024" datatype="html"> <trans-unit id="6004588582437169024" datatype="html">
<source>Current week</source> <source>Current week</source>
<target state="new">Current week</target> <target state="translated">Huidige week</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts</context> <context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts</context>
<context context-type="linenumber">191</context> <context context-type="linenumber">191</context>
@ -2079,7 +2079,7 @@
</trans-unit> </trans-unit>
<trans-unit id="7500665368930738879" datatype="html"> <trans-unit id="7500665368930738879" datatype="html">
<source>or start a discussion at</source> <source>or start a discussion at</source>
<target state="new">or start a discussion at</target> <target state="translated">of start een discussie op</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context>
<context context-type="linenumber">94</context> <context context-type="linenumber">94</context>
@ -2151,7 +2151,7 @@
</trans-unit> </trans-unit>
<trans-unit id="2003818202621229370" datatype="html"> <trans-unit id="2003818202621229370" datatype="html">
<source>Sustainable retirement income</source> <source>Sustainable retirement income</source>
<target state="new">Sustainable retirement income</target> <target state="translated">Duurzaam pensioeninkomen</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context>
<context context-type="linenumber">41</context> <context context-type="linenumber">41</context>
@ -2323,7 +2323,7 @@
</trans-unit> </trans-unit>
<trans-unit id="1531212547408073567" datatype="html"> <trans-unit id="1531212547408073567" datatype="html">
<source>contact us</source> <source>contact us</source>
<target state="new">contact us</target> <target state="translated">contacteer ons</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context>
<context context-type="linenumber">336</context> <context context-type="linenumber">336</context>
@ -2399,7 +2399,7 @@
</trans-unit> </trans-unit>
<trans-unit id="79310201207169632" datatype="html"> <trans-unit id="79310201207169632" datatype="html">
<source>Exclude from Analysis</source> <source>Exclude from Analysis</source>
<target state="new">Exclude from Analysis</target> <target state="translated">Uitsluiten van analyse</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html</context> <context context-type="sourcefile">apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html</context>
<context context-type="linenumber">90</context> <context context-type="linenumber">90</context>
@ -2423,7 +2423,7 @@
</trans-unit> </trans-unit>
<trans-unit id="7934616470747135563" datatype="html"> <trans-unit id="7934616470747135563" datatype="html">
<source>Latest activities</source> <source>Latest activities</source>
<target state="new">Latest activities</target> <target state="translated">Laatste activiteiten</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/public/public-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/public/public-page.html</context>
<context context-type="linenumber">210</context> <context context-type="linenumber">210</context>
@ -2539,7 +2539,7 @@
</trans-unit> </trans-unit>
<trans-unit id="5211792611718918888" datatype="html"> <trans-unit id="5211792611718918888" datatype="html">
<source>annual interest rate</source> <source>annual interest rate</source>
<target state="new">annual interest rate</target> <target state="translated">jaarlijkse rente</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context>
<context context-type="linenumber">185</context> <context context-type="linenumber">185</context>
@ -2659,7 +2659,7 @@
</trans-unit> </trans-unit>
<trans-unit id="7341990227686441824" datatype="html"> <trans-unit id="7341990227686441824" datatype="html">
<source>Could not validate form</source> <source>Could not validate form</source>
<target state="new">Could not validate form</target> <target state="translated">Het formulier kon niet worden gevalideerd.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts</context> <context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts</context>
<context context-type="linenumber">554</context> <context context-type="linenumber">554</context>
@ -2895,7 +2895,7 @@
</trans-unit> </trans-unit>
<trans-unit id="8966698274727122602" datatype="html"> <trans-unit id="8966698274727122602" datatype="html">
<source>Authentication</source> <source>Authentication</source>
<target state="new">Authentication</target> <target state="translated">Authenticatie</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html</context> <context context-type="sourcefile">apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html</context>
<context context-type="linenumber">54</context> <context context-type="linenumber">54</context>
@ -3047,7 +3047,7 @@
</trans-unit> </trans-unit>
<trans-unit id="3227075298129844075" datatype="html"> <trans-unit id="3227075298129844075" datatype="html">
<source>If you retire today, you would be able to withdraw</source> <source>If you retire today, you would be able to withdraw</source>
<target state="new">If you retire today, you would be able to withdraw</target> <target state="translated">Als u vandaag met pensioen gaat, kunt u</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context>
<context context-type="linenumber">68</context> <context context-type="linenumber">68</context>
@ -3115,7 +3115,7 @@
</trans-unit> </trans-unit>
<trans-unit id="7763941937414903315" datatype="html"> <trans-unit id="7763941937414903315" datatype="html">
<source>Looking for a student discount?</source> <source>Looking for a student discount?</source>
<target state="new">Looking for a student discount?</target> <target state="translated">Op zoek naar studentenkorting?</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context>
<context context-type="linenumber">342</context> <context context-type="linenumber">342</context>
@ -3343,7 +3343,7 @@
</trans-unit> </trans-unit>
<trans-unit id="9218541487912911620" datatype="html"> <trans-unit id="9218541487912911620" datatype="html">
<source>No Activities</source> <source>No Activities</source>
<target state="new">No Activities</target> <target state="translated">Geen activiteiten</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/admin-market-data.component.ts</context> <context context-type="sourcefile">apps/client/src/app/components/admin-market-data/admin-market-data.component.ts</context>
<context context-type="linenumber">146</context> <context context-type="linenumber">146</context>
@ -3359,7 +3359,7 @@
</trans-unit> </trans-unit>
<trans-unit id="936060984157466006" datatype="html"> <trans-unit id="936060984157466006" datatype="html">
<source>Everything in <x id="START_TAG_STRONG" ctype="x-strong" equiv-text="&lt;strong&gt;"/>Basic<x id="CLOSE_TAG_STRONG" ctype="x-strong" equiv-text="&lt;/strong&gt;"/>, plus</source> <source>Everything in <x id="START_TAG_STRONG" ctype="x-strong" equiv-text="&lt;strong&gt;"/>Basic<x id="CLOSE_TAG_STRONG" ctype="x-strong" equiv-text="&lt;/strong&gt;"/>, plus</source>
<target state="new">Everything in <x id="START_TAG_STRONG" ctype="x-strong" equiv-text="&lt;strong&gt;"/>Basic<x id="CLOSE_TAG_STRONG" ctype="x-strong" equiv-text="&lt;/strong&gt;"/>, plus</target> <target state="translated">Alles van <x id="START_TAG_STRONG" ctype="x-strong" equiv-text="&lt;strong&gt;"/>Basic<x id="CLOSE_TAG_STRONG" ctype="x-strong" equiv-text="&lt;/strong&gt;"/>, plus</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context>
<context context-type="linenumber">199</context> <context context-type="linenumber">199</context>
@ -3619,7 +3619,7 @@
</trans-unit> </trans-unit>
<trans-unit id="7498591289549626867" datatype="html"> <trans-unit id="7498591289549626867" datatype="html">
<source>Could not save asset profile</source> <source>Could not save asset profile</source>
<target state="new">Could not save asset profile</target> <target state="translated">Kon het assetprofiel niet opslaan</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts</context> <context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts</context>
<context context-type="linenumber">588</context> <context context-type="linenumber">588</context>
@ -3823,7 +3823,7 @@
</trans-unit> </trans-unit>
<trans-unit id="7702646444963497962" datatype="html"> <trans-unit id="7702646444963497962" datatype="html">
<source>By</source> <source>By</source>
<target state="new">By</target> <target state="translated">Door</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context>
<context context-type="linenumber">139</context> <context context-type="linenumber">139</context>
@ -3839,7 +3839,7 @@
</trans-unit> </trans-unit>
<trans-unit id="4340477809050781416" datatype="html"> <trans-unit id="4340477809050781416" datatype="html">
<source>Current year</source> <source>Current year</source>
<target state="new">Current year</target> <target state="translated">Huidig jaar</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts</context> <context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts</context>
<context context-type="linenumber">199</context> <context context-type="linenumber">199</context>
@ -3875,7 +3875,7 @@
</trans-unit> </trans-unit>
<trans-unit id="8319378030525016917" datatype="html"> <trans-unit id="8319378030525016917" datatype="html">
<source>Asset profile has been saved</source> <source>Asset profile has been saved</source>
<target state="new">Asset profile has been saved</target> <target state="translated">Het activaprofiel is opgeslagen.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts</context> <context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts</context>
<context context-type="linenumber">578</context> <context context-type="linenumber">578</context>
@ -4067,7 +4067,7 @@
</trans-unit> </trans-unit>
<trans-unit id="1468015720862673946" datatype="html"> <trans-unit id="1468015720862673946" datatype="html">
<source>View Details</source> <source>View Details</source>
<target state="new">View Details</target> <target state="translated">Bekijk details</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/admin-users/admin-users.html</context> <context context-type="sourcefile">apps/client/src/app/components/admin-users/admin-users.html</context>
<context context-type="linenumber">225</context> <context context-type="linenumber">225</context>
@ -4203,7 +4203,7 @@
</trans-unit> </trans-unit>
<trans-unit id="3556628518893194463" datatype="html"> <trans-unit id="3556628518893194463" datatype="html">
<source>per week</source> <source>per week</source>
<target state="new">per week</target> <target state="translated">per week</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/subscription-interstitial-dialog/subscription-interstitial-dialog.html</context> <context context-type="sourcefile">apps/client/src/app/components/subscription-interstitial-dialog/subscription-interstitial-dialog.html</context>
<context context-type="linenumber">130</context> <context context-type="linenumber">130</context>
@ -4227,7 +4227,7 @@
</trans-unit> </trans-unit>
<trans-unit id="577204259483334667" datatype="html"> <trans-unit id="577204259483334667" datatype="html">
<source>and we share aggregated <x id="START_LINK" ctype="x-a" equiv-text="&lt;a title=&quot;Open Startup&quot; [routerLink]=&quot;routerLinkOpenStartup&quot; &gt;"/>key metrics<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/> of the platform’s performance</source> <source>and we share aggregated <x id="START_LINK" ctype="x-a" equiv-text="&lt;a title=&quot;Open Startup&quot; [routerLink]=&quot;routerLinkOpenStartup&quot; &gt;"/>key metrics<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/> of the platform’s performance</source>
<target state="new">and we share aggregated <x id="START_LINK" ctype="x-a" equiv-text="&lt;a title=&quot;Open Startup&quot; [routerLink]=&quot;routerLinkOpenStartup&quot; &gt;"/>key metrics<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/> of the platform’s performance</target> <target state="translated">en we delen geaggregeerde belangrijke <x id="START_LINK" ctype="x-a" equiv-text="&lt;a title=&quot;Open Startup&quot; [routerLink]=&quot;routerLinkOpenStartup&quot; &gt;"/>prestatiegegevens<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/> van het platform</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context>
<context context-type="linenumber">32</context> <context context-type="linenumber">32</context>
@ -4271,7 +4271,7 @@
</trans-unit> </trans-unit>
<trans-unit id="5271053765919315173" datatype="html"> <trans-unit id="5271053765919315173" datatype="html">
<source>Website of Thomas Kaul</source> <source>Website of Thomas Kaul</source>
<target state="new">Website of Thomas Kaul</target> <target state="translated">Website van Thomas Kaul</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context>
<context context-type="linenumber">44</context> <context context-type="linenumber">44</context>
@ -4451,7 +4451,7 @@
</trans-unit> </trans-unit>
<trans-unit id="2145636458848553570" datatype="html"> <trans-unit id="2145636458848553570" datatype="html">
<source>Sign in with OpenID Connect</source> <source>Sign in with OpenID Connect</source>
<target state="new">Sign in with OpenID Connect</target> <target state="translated">Meld je aan met OpenID Connect</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html</context> <context context-type="sourcefile">apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html</context>
<context context-type="linenumber">55</context> <context context-type="linenumber">55</context>
@ -4543,7 +4543,7 @@
</trans-unit> </trans-unit>
<trans-unit id="5289957034780335504" datatype="html"> <trans-unit id="5289957034780335504" datatype="html">
<source>The source code is fully available as <x id="START_LINK" ctype="x-a" equiv-text="&lt;a href=&quot;https://github.com/ghostfolio/ghostfolio&quot; title=&quot;Find Ghostfolio on GitHub&quot; &gt;"/>open source software<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/> (OSS) under the <x id="START_LINK_1" equiv-text="&lt;a href=&quot;https://www.gnu.org/licenses/agpl-3.0.html&quot; title=&quot;GNU Affero General Public License&quot; &gt;"/>AGPL-3.0 license<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/></source> <source>The source code is fully available as <x id="START_LINK" ctype="x-a" equiv-text="&lt;a href=&quot;https://github.com/ghostfolio/ghostfolio&quot; title=&quot;Find Ghostfolio on GitHub&quot; &gt;"/>open source software<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/> (OSS) under the <x id="START_LINK_1" equiv-text="&lt;a href=&quot;https://www.gnu.org/licenses/agpl-3.0.html&quot; title=&quot;GNU Affero General Public License&quot; &gt;"/>AGPL-3.0 license<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/></source>
<target state="new">The source code is fully available as <x id="START_LINK" ctype="x-a" equiv-text="&lt;a href=&quot;https://github.com/ghostfolio/ghostfolio&quot; title=&quot;Find Ghostfolio on GitHub&quot; &gt;"/>open source software<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/> (OSS) under the <x id="START_LINK_1" equiv-text="&lt;a href=&quot;https://www.gnu.org/licenses/agpl-3.0.html&quot; title=&quot;GNU Affero General Public License&quot; &gt;"/>AGPL-3.0 license<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/></target> <target state="translated">De broncode is volledig beschikbaar als <x id="START_LINK" ctype="x-a" equiv-text="&lt;a href=&quot;https://github.com/ghostfolio/ghostfolio&quot; title=&quot;Find Ghostfolio on GitHub&quot; &gt;"/>open source software<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/> (OSS) onder de <x id="START_LINK_1" equiv-text="&lt;a href=&quot;https://www.gnu.org/licenses/agpl-3.0.html&quot; title=&quot;GNU Affero General Public License&quot; &gt;"/>AGPL-3.0-licentie<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/></target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context>
<context context-type="linenumber">16</context> <context context-type="linenumber">16</context>
@ -4615,7 +4615,7 @@
</trans-unit> </trans-unit>
<trans-unit id="3004519800638083911" datatype="html"> <trans-unit id="3004519800638083911" datatype="html">
<source>this is projected to increase to</source> <source>this is projected to increase to</source>
<target state="new">this is projected to increase to</target> <target state="translated">zal dit naar verwachting stijgen tot</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context>
<context context-type="linenumber">147</context> <context context-type="linenumber">147</context>
@ -4667,7 +4667,7 @@
</trans-unit> </trans-unit>
<trans-unit id="3627006945295714424" datatype="html"> <trans-unit id="3627006945295714424" datatype="html">
<source>Job ID</source> <source>Job ID</source>
<target state="new">Job ID</target> <target state="translated">Opdracht ID</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/admin-jobs/admin-jobs.html</context> <context context-type="sourcefile">apps/client/src/app/components/admin-jobs/admin-jobs.html</context>
<context context-type="linenumber">34</context> <context context-type="linenumber">34</context>
@ -4751,7 +4751,7 @@
</trans-unit> </trans-unit>
<trans-unit id="8553460997100418147" datatype="html"> <trans-unit id="8553460997100418147" datatype="html">
<source>for</source> <source>for</source>
<target state="new">for</target> <target state="translated">voor</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/subscription-interstitial-dialog/subscription-interstitial-dialog.html</context> <context context-type="sourcefile">apps/client/src/app/components/subscription-interstitial-dialog/subscription-interstitial-dialog.html</context>
<context context-type="linenumber">128</context> <context context-type="linenumber">128</context>
@ -4775,7 +4775,7 @@
</trans-unit> </trans-unit>
<trans-unit id="5134951682994822188" datatype="html"> <trans-unit id="5134951682994822188" datatype="html">
<source>Could not parse scraper configuration</source> <source>Could not parse scraper configuration</source>
<target state="new">Could not parse scraper configuration</target> <target state="translated">De scraperconfiguratie kon niet worden geparseerd</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts</context> <context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts</context>
<context context-type="linenumber">509</context> <context context-type="linenumber">509</context>
@ -4819,7 +4819,7 @@
</trans-unit> </trans-unit>
<trans-unit id="9187635907883145155" datatype="html"> <trans-unit id="9187635907883145155" datatype="html">
<source>Edit access</source> <source>Edit access</source>
<target state="new">Edit access</target> <target state="translated">Toegang bewerken</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.html</context> <context context-type="sourcefile">apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.html</context>
<context context-type="linenumber">11</context> <context context-type="linenumber">11</context>
@ -4891,7 +4891,7 @@
</trans-unit> </trans-unit>
<trans-unit id="8984201769958269296" datatype="html"> <trans-unit id="8984201769958269296" datatype="html">
<source>Get access to 80’000+ tickers from over 50 exchanges</source> <source>Get access to 80’000+ tickers from over 50 exchanges</source>
<target state="new">Get access to 80’000+ tickers from over 50 exchanges</target> <target state="translated">Krijg toegang tot meer dan 80.000+ tickers van meer dan 50 beurzen</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/subscription-interstitial-dialog/subscription-interstitial-dialog.html</context> <context context-type="sourcefile">apps/client/src/app/components/subscription-interstitial-dialog/subscription-interstitial-dialog.html</context>
<context context-type="linenumber">84</context> <context context-type="linenumber">84</context>
@ -5075,7 +5075,7 @@
</trans-unit> </trans-unit>
<trans-unit id="8014012170270529279" datatype="html"> <trans-unit id="8014012170270529279" datatype="html">
<source>less than</source> <source>less than</source>
<target state="new">less than</target> <target state="translated">minder dan</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/subscription-interstitial-dialog/subscription-interstitial-dialog.html</context> <context context-type="sourcefile">apps/client/src/app/components/subscription-interstitial-dialog/subscription-interstitial-dialog.html</context>
<context context-type="linenumber">129</context> <context context-type="linenumber">129</context>
@ -5365,7 +5365,7 @@
</trans-unit> </trans-unit>
<trans-unit id="4257439615478050183" datatype="html"> <trans-unit id="4257439615478050183" datatype="html">
<source>Ghostfolio Status</source> <source>Ghostfolio Status</source>
<target state="new">Ghostfolio Status</target> <target state="translated">Ghostfolio Status</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context>
<context context-type="linenumber">62</context> <context context-type="linenumber">62</context>
@ -5373,7 +5373,7 @@
</trans-unit> </trans-unit>
<trans-unit id="4275978599610634089" datatype="html"> <trans-unit id="4275978599610634089" datatype="html">
<source>with your university e-mail address</source> <source>with your university e-mail address</source>
<target state="new">with your university e-mail address</target> <target state="translated">met uw universitaire e-mailadres</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context>
<context context-type="linenumber">348</context> <context context-type="linenumber">348</context>
@ -5393,7 +5393,7 @@
</trans-unit> </trans-unit>
<trans-unit id="70768492340592330" datatype="html"> <trans-unit id="70768492340592330" datatype="html">
<source>and a safe withdrawal rate (SWR) of</source> <source>and a safe withdrawal rate (SWR) of</source>
<target state="new">and a safe withdrawal rate (SWR) of</target> <target state="translated">en een veilige opnameratio (SWR) van</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context>
<context context-type="linenumber">108</context> <context context-type="linenumber">108</context>
@ -5557,7 +5557,7 @@
</trans-unit> </trans-unit>
<trans-unit id="5276907121760788823" datatype="html"> <trans-unit id="5276907121760788823" datatype="html">
<source>Request it</source> <source>Request it</source>
<target state="new">Request it</target> <target state="translated">Aanvragen</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context>
<context context-type="linenumber">344</context> <context context-type="linenumber">344</context>
@ -5613,7 +5613,7 @@
</trans-unit> </trans-unit>
<trans-unit id="page.fire.projected.1" datatype="html"> <trans-unit id="page.fire.projected.1" datatype="html">
<source>,</source> <source>,</source>
<target state="new">,</target> <target state="translated">,</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context>
<context context-type="linenumber">145</context> <context context-type="linenumber">145</context>
@ -5629,7 +5629,7 @@
</trans-unit> </trans-unit>
<trans-unit id="4905798562247431262" datatype="html"> <trans-unit id="4905798562247431262" datatype="html">
<source>per month</source> <source>per month</source>
<target state="new">per month</target> <target state="translated">per maand</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context>
<context context-type="linenumber">94</context> <context context-type="linenumber">94</context>
@ -5821,7 +5821,7 @@
</trans-unit> </trans-unit>
<trans-unit id="2575998129003872734" datatype="html"> <trans-unit id="2575998129003872734" datatype="html">
<source>Argentina</source> <source>Argentina</source>
<target state="new">Argentina</target> <target state="translated">Argentini&euml;</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">libs/ui/src/lib/i18n.ts</context> <context context-type="sourcefile">libs/ui/src/lib/i18n.ts</context>
<context context-type="linenumber">78</context> <context context-type="linenumber">78</context>
@ -5877,7 +5877,7 @@
</trans-unit> </trans-unit>
<trans-unit id="858192247408211331" datatype="html"> <trans-unit id="858192247408211331" datatype="html">
<source>here</source> <source>here</source>
<target state="new">here</target> <target state="translated">hier</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context>
<context context-type="linenumber">347</context> <context context-type="linenumber">347</context>
@ -5885,7 +5885,7 @@
</trans-unit> </trans-unit>
<trans-unit id="1600023202562292052" datatype="html"> <trans-unit id="1600023202562292052" datatype="html">
<source>Close Holding</source> <source>Close Holding</source>
<target state="new">Close Holding</target> <target state="translated">Sluit Holding</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html</context> <context context-type="sourcefile">apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html</context>
<context context-type="linenumber">441</context> <context context-type="linenumber">441</context>
@ -6266,7 +6266,7 @@
</trans-unit> </trans-unit>
<trans-unit id="5707368132268957392" datatype="html"> <trans-unit id="5707368132268957392" datatype="html">
<source>Include in</source> <source>Include in</source>
<target state="new">Include in</target> <target state="translated">Opnemen in</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html</context> <context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html</context>
<context context-type="linenumber">374</context> <context context-type="linenumber">374</context>
@ -6550,7 +6550,7 @@
</trans-unit> </trans-unit>
<trans-unit id="3477953895055172777" datatype="html"> <trans-unit id="3477953895055172777" datatype="html">
<source>View Holding</source> <source>View Holding</source>
<target state="new">View Holding</target> <target state="translated">Bekijk Holding</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">libs/ui/src/lib/activities-table/activities-table.component.html</context> <context context-type="sourcefile">libs/ui/src/lib/activities-table/activities-table.component.html</context>
<context context-type="linenumber">450</context> <context context-type="linenumber">450</context>
@ -6694,7 +6694,7 @@
</trans-unit> </trans-unit>
<trans-unit id="4762855117875399861" datatype="html"> <trans-unit id="4762855117875399861" datatype="html">
<source>Oops! Could not update access.</source> <source>Oops! Could not update access.</source>
<target state="new">Oops! Could not update access.</target> <target state="translated">Oops! Kan de toegang niet updaten.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.component.ts</context> <context context-type="sourcefile">apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.component.ts</context>
<context context-type="linenumber">178</context> <context context-type="linenumber">178</context>
@ -6702,7 +6702,7 @@
</trans-unit> </trans-unit>
<trans-unit id="1355312194390410495" datatype="html"> <trans-unit id="1355312194390410495" datatype="html">
<source>, based on your total assets of</source> <source>, based on your total assets of</source>
<target state="new">, based on your total assets of</target> <target state="translated"> opnemen, dit is gebaseerd op uw totale vermogen van</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context>
<context context-type="linenumber">96</context> <context context-type="linenumber">96</context>
@ -6822,7 +6822,7 @@
</trans-unit> </trans-unit>
<trans-unit id="4145496584631696119" datatype="html"> <trans-unit id="4145496584631696119" datatype="html">
<source>Role</source> <source>Role</source>
<target state="new">Role</target> <target state="translated">Rol</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html</context> <context context-type="sourcefile">apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html</context>
<context context-type="linenumber">33</context> <context context-type="linenumber">33</context>
@ -6862,7 +6862,7 @@
</trans-unit> </trans-unit>
<trans-unit id="6602358241522477056" datatype="html"> <trans-unit id="6602358241522477056" datatype="html">
<source>If you plan to open an account at</source> <source>If you plan to open an account at</source>
<target state="new">If you plan to open an account at</target> <target state="translated">Als u van plan bent een rekening te openen bij</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context>
<context context-type="linenumber">312</context> <context context-type="linenumber">312</context>
@ -6894,7 +6894,7 @@
</trans-unit> </trans-unit>
<trans-unit id="6664504469290651320" datatype="html"> <trans-unit id="6664504469290651320" datatype="html">
<source>send an e-mail to</source> <source>send an e-mail to</source>
<target state="new">send an e-mail to</target> <target state="translated">stuur een e-mail naar</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context>
<context context-type="linenumber">87</context> <context context-type="linenumber">87</context>
@ -6966,7 +6966,7 @@
</trans-unit> </trans-unit>
<trans-unit id="2878377610946588870" datatype="html"> <trans-unit id="2878377610946588870" datatype="html">
<source>, assuming a</source> <source>, assuming a</source>
<target state="new">, assuming a</target> <target state="translated">, uitgaande van</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context>
<context context-type="linenumber">174</context> <context context-type="linenumber">174</context>
@ -7054,7 +7054,7 @@
</trans-unit> </trans-unit>
<trans-unit id="3528767106831563012" datatype="html"> <trans-unit id="3528767106831563012" datatype="html">
<source>Ghostfolio is a lightweight wealth management application for individuals to keep track of stocks, ETFs or cryptocurrencies and make solid, data-driven investment decisions.</source> <source>Ghostfolio is a lightweight wealth management application for individuals to keep track of stocks, ETFs or cryptocurrencies and make solid, data-driven investment decisions.</source>
<target state="new">Ghostfolio is a lightweight wealth management application for individuals to keep track of stocks, ETFs or cryptocurrencies and make solid, data-driven investment decisions.</target> <target state="translated">Ghostfolio is een gebruiksvriendelijke applicatie voor vermogensbeheer waarmee particulieren hun aandelen, ETF's of cryptovaluta kunnen volgen en weloverwogen, datagestuurde beleggingsbeslissingen kunnen nemen.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context>
<context context-type="linenumber">10</context> <context context-type="linenumber">10</context>
@ -7384,7 +7384,7 @@
</trans-unit> </trans-unit>
<trans-unit id="7825231215382064101" datatype="html"> <trans-unit id="7825231215382064101" datatype="html">
<source>Change with currency effect</source> <source>Change with currency effect</source>
<target state="new">Change with currency effect</target> <target state="translated">Verandering met valuta-effect</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/analysis/analysis-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/portfolio/analysis/analysis-page.html</context>
<context context-type="linenumber">116</context> <context context-type="linenumber">116</context>
@ -7524,7 +7524,7 @@
</trans-unit> </trans-unit>
<trans-unit id="1325095699053123251" datatype="html"> <trans-unit id="1325095699053123251" datatype="html">
<source>The project has been initiated by</source> <source>The project has been initiated by</source>
<target state="new">The project has been initiated by</target> <target state="translated">Het project is geïnitieerd door</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context>
<context context-type="linenumber">40</context> <context context-type="linenumber">40</context>
@ -7548,7 +7548,7 @@
</trans-unit> </trans-unit>
<trans-unit id="5004550577313573215" datatype="html"> <trans-unit id="5004550577313573215" datatype="html">
<source>Total amount</source> <source>Total amount</source>
<target state="new">Total amount</target> <target state="translated">Totaal bedrag</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/analysis/analysis-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/portfolio/analysis/analysis-page.html</context>
<context context-type="linenumber">95</context> <context context-type="linenumber">95</context>
@ -7640,7 +7640,7 @@
</trans-unit> </trans-unit>
<trans-unit id="6752851341939241310" datatype="html"> <trans-unit id="6752851341939241310" datatype="html">
<source>Find account, holding or page...</source> <source>Find account, holding or page...</source>
<target state="new">Find account, holding or page...</target> <target state="translated">Vindt een account, holding of pagina...</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">libs/ui/src/lib/assistant/assistant.component.ts</context> <context context-type="sourcefile">libs/ui/src/lib/assistant/assistant.component.ts</context>
<context context-type="linenumber">115</context> <context context-type="linenumber">115</context>
@ -7890,7 +7890,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.feeRatioTotalInvestmentVolume" datatype="html"> <trans-unit id="rule.feeRatioTotalInvestmentVolume" datatype="html">
<source>Fee Ratio</source> <source>Fee Ratio</source>
<target state="new">Fee Ratio</target> <target state="translated">Vergoedingsverhouding</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">152</context> <context context-type="linenumber">152</context>
@ -7898,7 +7898,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.feeRatioTotalInvestmentVolume.false" datatype="html"> <trans-unit id="rule.feeRatioTotalInvestmentVolume.false" datatype="html">
<source>The fees do exceed ${thresholdMax}% of your total investment volume (${feeRatio}%)</source> <source>The fees do exceed ${thresholdMax}% of your total investment volume (${feeRatio}%)</source>
<target state="new">The fees do exceed ${thresholdMax}% of your total investment volume (${feeRatio}%)</target> <target state="translated">De kosten overschrijden ${thresholdMax}% van uw totale investeringsvolume (${feeRatio}%)</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">154</context> <context context-type="linenumber">154</context>
@ -7906,7 +7906,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.feeRatioTotalInvestmentVolume.true" datatype="html"> <trans-unit id="rule.feeRatioTotalInvestmentVolume.true" datatype="html">
<source>The fees do not exceed ${thresholdMax}% of your total investment volume (${feeRatio}%)</source> <source>The fees do not exceed ${thresholdMax}% of your total investment volume (${feeRatio}%)</source>
<target state="new">The fees do not exceed ${thresholdMax}% of your total investment volume (${feeRatio}%)</target> <target state="translated">De kosten bedragen maximaal ${thresholdMax}% van uw totale investeringsvolume (${feeRatio}%)</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">158</context> <context context-type="linenumber">158</context>
@ -8064,7 +8064,7 @@
</trans-unit> </trans-unit>
<trans-unit id="7383756232563820625" datatype="html"> <trans-unit id="7383756232563820625" datatype="html">
<source>Current month</source> <source>Current month</source>
<target state="new">Current month</target> <target state="translated">Huidige maand</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts</context> <context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts</context>
<context context-type="linenumber">195</context> <context context-type="linenumber">195</context>
@ -8249,7 +8249,7 @@
</trans-unit> </trans-unit>
<trans-unit id="5199695670214400859" datatype="html"> <trans-unit id="5199695670214400859" datatype="html">
<source>If you encounter a bug, would like to suggest an improvement or a new <x id="START_LINK" ctype="x-a" equiv-text="&lt;a [routerLink]=&quot;routerLinkFeatures&quot;&gt;"/>feature<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/>, please join the Ghostfolio <x id="START_LINK_1" equiv-text="&lt;a href=&quot;https://join.slack.com/t/ghostfolio/shared_invite/zt-vsaan64h-F_I0fEo5M0P88lP9ibCxFg&quot; title=&quot;Join the Ghostfolio Slack community&quot; &gt;"/>Slack<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/> community, post to <x id="START_LINK_2" equiv-text="&lt;a href=&quot;https://x.com/ghostfolio_&quot; title=&quot;Post to Ghostfolio on X (formerly Twitter)&quot; &gt;"/>@ghostfolio_<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/></source> <source>If you encounter a bug, would like to suggest an improvement or a new <x id="START_LINK" ctype="x-a" equiv-text="&lt;a [routerLink]=&quot;routerLinkFeatures&quot;&gt;"/>feature<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/>, please join the Ghostfolio <x id="START_LINK_1" equiv-text="&lt;a href=&quot;https://join.slack.com/t/ghostfolio/shared_invite/zt-vsaan64h-F_I0fEo5M0P88lP9ibCxFg&quot; title=&quot;Join the Ghostfolio Slack community&quot; &gt;"/>Slack<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/> community, post to <x id="START_LINK_2" equiv-text="&lt;a href=&quot;https://x.com/ghostfolio_&quot; title=&quot;Post to Ghostfolio on X (formerly Twitter)&quot; &gt;"/>@ghostfolio_<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/></source>
<target state="new">If you encounter a bug, would like to suggest an improvement or a new <x id="START_LINK" ctype="x-a" equiv-text="&lt;a [routerLink]=&quot;routerLinkFeatures&quot;&gt;"/>feature<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/>, please join the Ghostfolio <x id="START_LINK_1" equiv-text="&lt;a href=&quot;https://join.slack.com/t/ghostfolio/shared_invite/zt-vsaan64h-F_I0fEo5M0P88lP9ibCxFg&quot; title=&quot;Join the Ghostfolio Slack community&quot; &gt;"/>Slack<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/> community, post to <x id="START_LINK_2" equiv-text="&lt;a href=&quot;https://x.com/ghostfolio_&quot; title=&quot;Post to Ghostfolio on X (formerly Twitter)&quot; &gt;"/>@ghostfolio_<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/></target> <target state="translated">Als je een bug tegenkomt, een verbetering wilt voorstellen of een nieuwe <x id="START_LINK" ctype="x-a" equiv-text="&lt;a [routerLink]=&quot;routerLinkFeatures&quot;&gt;"/>functie<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/> wilt toevoegen, word dan lid van de Ghostfolio <x id="START_LINK_1" equiv-text="&lt;a href=&quot;https://join.slack.com/t/ghostfolio/shared_invite/zt-vsaan64h-F_I0fEo5M0P88lP9ibCxFg&quot; title=&quot;Join the Ghostfolio Slack community&quot; &gt;"/>Slack-community<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/>. Stuur een bericht naar <x id="START_LINK_2" equiv-text="&lt;a href=&quot;https://x.com/ghostfolio_&quot; title=&quot;Post to Ghostfolio on X (formerly Twitter)&quot; &gt;"/>@ghostfolio_<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/></target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context>
<context context-type="linenumber">69</context> <context context-type="linenumber">69</context>
@ -8373,7 +8373,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.liquidity.category" datatype="html"> <trans-unit id="rule.liquidity.category" datatype="html">
<source>Liquidity</source> <source>Liquidity</source>
<target state="new">Liquidity</target> <target state="translated">Liquiditeit</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">70</context> <context context-type="linenumber">70</context>
@ -8381,7 +8381,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.liquidityBuyingPower" datatype="html"> <trans-unit id="rule.liquidityBuyingPower" datatype="html">
<source>Buying Power</source> <source>Buying Power</source>
<target state="new">Buying Power</target> <target state="translated">Koopkracht</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">71</context> <context context-type="linenumber">71</context>
@ -8389,7 +8389,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.liquidityBuyingPower.false.min" datatype="html"> <trans-unit id="rule.liquidityBuyingPower.false.min" datatype="html">
<source>Your buying power is below ${thresholdMin} ${baseCurrency}</source> <source>Your buying power is below ${thresholdMin} ${baseCurrency}</source>
<target state="new">Your buying power is below ${thresholdMin} ${baseCurrency}</target> <target state="translated">Uw koopkracht ligt onder ${thresholdMin} ${baseCurrency}</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">73</context> <context context-type="linenumber">73</context>
@ -8397,7 +8397,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.liquidityBuyingPower.false.zero" datatype="html"> <trans-unit id="rule.liquidityBuyingPower.false.zero" datatype="html">
<source>Your buying power is 0 ${baseCurrency}</source> <source>Your buying power is 0 ${baseCurrency}</source>
<target state="new">Your buying power is 0 ${baseCurrency}</target> <target state="translated">Uw koopkracht is 0 ${baseCurrency}</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">77</context> <context context-type="linenumber">77</context>
@ -8405,7 +8405,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.liquidityBuyingPower.true" datatype="html"> <trans-unit id="rule.liquidityBuyingPower.true" datatype="html">
<source>Your buying power exceeds ${thresholdMin} ${baseCurrency}</source> <source>Your buying power exceeds ${thresholdMin} ${baseCurrency}</source>
<target state="new">Your buying power exceeds ${thresholdMin} ${baseCurrency}</target> <target state="translated">Uw koopkracht overschrijdt ${thresholdMin} ${baseCurrency}</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">80</context> <context context-type="linenumber">80</context>
@ -8709,7 +8709,7 @@
</trans-unit> </trans-unit>
<trans-unit id="339860602695747533" datatype="html"> <trans-unit id="339860602695747533" datatype="html">
<source>Registration Date</source> <source>Registration Date</source>
<target state="new">Registration Date</target> <target state="translated">Registratiedatum</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html</context> <context context-type="sourcefile">apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html</context>
<context context-type="linenumber">45</context> <context context-type="linenumber">45</context>

11
libs/common/src/lib/dtos/create-asset-profile.dto.ts

@ -5,7 +5,6 @@ import {
IsArray, IsArray,
IsBoolean, IsBoolean,
IsEnum, IsEnum,
IsObject,
IsOptional, IsOptional,
IsString, IsString,
IsUrl IsUrl
@ -66,10 +65,6 @@ export class CreateAssetProfileDto {
@IsString() @IsString()
name?: string; name?: string;
@IsObject()
@IsOptional()
scraperConfiguration?: Prisma.InputJsonObject;
@IsArray() @IsArray()
@IsOptional() @IsOptional()
sectors?: Prisma.InputJsonArray; sectors?: Prisma.InputJsonArray;
@ -77,12 +72,6 @@ export class CreateAssetProfileDto {
@IsString() @IsString()
symbol: string; symbol: string;
@IsObject()
@IsOptional()
symbolMapping?: {
[dataProvider: string]: string;
};
@IsOptional() @IsOptional()
@IsUrl({ @IsUrl({
protocols: ['https'], protocols: ['https'],

4
libs/common/src/lib/interfaces/portfolio-summary.interface.ts

@ -6,10 +6,6 @@ export interface PortfolioSummary extends PortfolioPerformance {
annualizedPerformancePercent: number; annualizedPerformancePercent: number;
annualizedPerformancePercentWithCurrencyEffect: number; annualizedPerformancePercentWithCurrencyEffect: number;
cash: number; cash: number;
/** @deprecated use totalInvestmentValueWithCurrencyEffect instead */
committedFunds: number;
dateOfFirstActivity: Date; dateOfFirstActivity: Date;
dividendInBaseCurrency: number; dividendInBaseCurrency: number;
emergencyFund: { emergencyFund: {

7
libs/common/src/lib/interfaces/responses/export-response.interface.ts

@ -27,7 +27,12 @@ export interface ExportResponse {
> & { dataSource: DataSource; date: string; symbol: string })[]; > & { dataSource: DataSource; date: string; symbol: string })[];
assetProfiles: (Omit< assetProfiles: (Omit<
SymbolProfile, SymbolProfile,
'createdAt' | 'id' | 'updatedAt' | 'userId' | 'createdAt'
| 'id'
| 'scraperConfiguration'
| 'symbolMapping'
| 'updatedAt'
| 'userId'
> & { > & {
marketData: MarketData[]; marketData: MarketData[];
})[]; })[];

40
libs/common/src/lib/personal-finance-tools.ts

@ -33,6 +33,15 @@ export const personalFinanceTools: Product[] = [
origin: 'Switzerland', origin: 'Switzerland',
slogan: 'Simplicity for Complex Wealth' slogan: 'Simplicity for Complex Wealth'
}, },
{
founded: 2018,
hasFreePlan: false,
hasSelfHostingAbility: false,
key: 'altruist',
name: 'Altruist',
origin: 'United States',
slogan: 'The wealth platform built for independent advisors'
},
{ {
founded: 2023, founded: 2023,
hasFreePlan: false, hasFreePlan: false,
@ -116,6 +125,17 @@ export const personalFinanceTools: Product[] = [
origin: 'Switzerland', origin: 'Switzerland',
slogan: 'Schweizer Budget App für einfache & smarte Budgetplanung' slogan: 'Schweizer Budget App für einfache & smarte Budgetplanung'
}, },
{
founded: 2015,
hasFreePlan: true,
hasSelfHostingAbility: false,
key: 'boldin',
name: 'Boldin',
note: 'Originally named as NewRetirement',
origin: 'United States',
pricingPerYear: '$144',
slogan: 'Take control with retirement planning tools that begin with you'
},
{ {
key: 'budgetpulse', key: 'budgetpulse',
name: 'BudgetPulse', name: 'BudgetPulse',
@ -841,6 +861,16 @@ export const personalFinanceTools: Product[] = [
pricingPerYear: '$108', pricingPerYear: '$108',
slogan: 'Build Financial Plans You Love.' slogan: 'Build Financial Plans You Love.'
}, },
{
founded: 2022,
hasFreePlan: false,
hasSelfHostingAbility: false,
key: 'prostocktracker',
name: 'Pro Stock Tracker',
origin: 'United Kingdom',
pricingPerYear: '$60',
slogan: 'The stock portfolio tracker built for long-term investors'
},
{ {
founded: 2015, founded: 2015,
hasSelfHostingAbility: false, hasSelfHostingAbility: false,
@ -1014,6 +1044,16 @@ export const personalFinanceTools: Product[] = [
regions: ['Austria', 'Germany', 'Switzerland'], regions: ['Austria', 'Germany', 'Switzerland'],
slogan: 'Dein Vermögen immer im Blick' slogan: 'Dein Vermögen immer im Blick'
}, },
{
founded: 2025,
hasFreePlan: false,
hasSelfHostingAbility: false,
key: 'turbobulls',
name: 'Turbobulls',
origin: 'Romania',
pricingPerYear: '€39.99',
slogan: 'Your complete financial dashboard. Actually private.'
},
{ {
hasFreePlan: true, hasFreePlan: true,
hasSelfHostingAbility: false, hasSelfHostingAbility: false,

4
libs/ui/src/lib/assistant/assistant.html

@ -197,7 +197,7 @@
mat-button mat-button
type="button" type="button"
[disabled]=" [disabled]="
!portfolioFilterForm.hasFilters() || portfolioFilterForm.disabled !portfolioFilterForm.hasFilters() || portfolioFilterForm.disabled()
" "
(click)="onResetFilters()" (click)="onResetFilters()"
> >
@ -210,7 +210,7 @@
type="button" type="button"
[disabled]=" [disabled]="
!portfolioFilterForm.filterForm.dirty || !portfolioFilterForm.filterForm.dirty ||
portfolioFilterForm.disabled portfolioFilterForm.disabled()
" "
(click)="onApplyFilters()" (click)="onApplyFilters()"
> >

677
package-lock.json

File diff suppressed because it is too large

26
package.json

@ -1,6 +1,6 @@
{ {
"name": "ghostfolio", "name": "ghostfolio",
"version": "2.244.0", "version": "2.247.0",
"homepage": "https://ghostfol.io", "homepage": "https://ghostfol.io",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"repository": "https://github.com/ghostfolio/ghostfolio", "repository": "https://github.com/ghostfolio/ghostfolio",
@ -134,7 +134,7 @@
"svgmap": "2.14.0", "svgmap": "2.14.0",
"tablemark": "4.1.0", "tablemark": "4.1.0",
"twitter-api-v2": "1.29.0", "twitter-api-v2": "1.29.0",
"yahoo-finance2": "3.13.0", "yahoo-finance2": "3.13.2",
"zone.js": "0.16.0" "zone.js": "0.16.0"
}, },
"devDependencies": { "devDependencies": {
@ -153,16 +153,16 @@
"@eslint/js": "9.35.0", "@eslint/js": "9.35.0",
"@nestjs/schematics": "11.0.9", "@nestjs/schematics": "11.0.9",
"@nestjs/testing": "11.1.14", "@nestjs/testing": "11.1.14",
"@nx/angular": "22.4.5", "@nx/angular": "22.5.3",
"@nx/eslint-plugin": "22.4.5", "@nx/eslint-plugin": "22.5.3",
"@nx/jest": "22.4.5", "@nx/jest": "22.5.3",
"@nx/js": "22.4.5", "@nx/js": "22.5.3",
"@nx/module-federation": "22.4.5", "@nx/module-federation": "22.5.3",
"@nx/nest": "22.4.5", "@nx/nest": "22.5.3",
"@nx/node": "22.4.5", "@nx/node": "22.5.3",
"@nx/storybook": "22.4.5", "@nx/storybook": "22.5.3",
"@nx/web": "22.4.5", "@nx/web": "22.5.3",
"@nx/workspace": "22.4.5", "@nx/workspace": "22.5.3",
"@schematics/angular": "21.1.1", "@schematics/angular": "21.1.1",
"@storybook/addon-docs": "10.1.10", "@storybook/addon-docs": "10.1.10",
"@storybook/angular": "10.1.10", "@storybook/angular": "10.1.10",
@ -187,7 +187,7 @@
"jest": "30.2.0", "jest": "30.2.0",
"jest-environment-jsdom": "30.2.0", "jest-environment-jsdom": "30.2.0",
"jest-preset-angular": "16.0.0", "jest-preset-angular": "16.0.0",
"nx": "22.4.5", "nx": "22.5.3",
"prettier": "3.8.1", "prettier": "3.8.1",
"prettier-plugin-organize-attributes": "1.0.0", "prettier-plugin-organize-attributes": "1.0.0",
"prisma": "6.19.0", "prisma": "6.19.0",

2
test/import/ok/penthouse-apartment.json

@ -21,10 +21,8 @@
"isin": null, "isin": null,
"marketData": [], "marketData": [],
"name": "Penthouse Apartment", "name": "Penthouse Apartment",
"scraperConfiguration": null,
"sectors": [], "sectors": [],
"symbol": "7e91b7d4-1430-4212-8380-289a06c9bbc1", "symbol": "7e91b7d4-1430-4212-8380-289a06c9bbc1",
"symbolMapping": {},
"url": null "url": null
} }
], ],

4
test/import/ok/sample.json

@ -41,10 +41,8 @@
"isin": null, "isin": null,
"marketData": [], "marketData": [],
"name": "Account Opening Fee", "name": "Account Opening Fee",
"scraperConfiguration": null,
"sectors": [], "sectors": [],
"symbol": "14a69cb9-1e31-43fa-b320-83703d8ed74b", "symbol": "14a69cb9-1e31-43fa-b320-83703d8ed74b",
"symbolMapping": {},
"url": null "url": null
}, },
{ {
@ -63,10 +61,8 @@
"isin": null, "isin": null,
"marketData": [], "marketData": [],
"name": "Penthouse Apartment", "name": "Penthouse Apartment",
"scraperConfiguration": null,
"sectors": [], "sectors": [],
"symbol": "7e91b7d4-1430-4212-8380-289a06c9bbc1", "symbol": "7e91b7d4-1430-4212-8380-289a06c9bbc1",
"symbolMapping": {},
"url": null "url": null
} }
], ],

Loading…
Cancel
Save