diff --git a/apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts b/apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts index 1af3c6d54..6007d29fb 100644 --- a/apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts +++ b/apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts @@ -5,84 +5,92 @@ import { publicRoutes } from '@ghostfolio/common/routes/routes'; import { translate } from '@ghostfolio/ui/i18n'; import { DataService } from '@ghostfolio/ui/services'; -import { Component, OnInit } from '@angular/core'; +import { + ChangeDetectionStrategy, + Component, + computed, + inject +} from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { ActivatedRoute, RouterModule } from '@angular/router'; @Component({ + changeDetection: ChangeDetectionStrategy.OnPush, host: { class: 'page' }, imports: [MatButtonModule, RouterModule], selector: 'gf-product-page', styleUrls: ['./product-page.scss'], templateUrl: './product-page.html' }) -export class GfProductPageComponent implements OnInit { - public key: string; - public price: number; - public product1: Product; - public product2: Product; - public routerLinkAbout = publicRoutes.about.routerLink; - public routerLinkFeatures = publicRoutes.features.routerLink; - public routerLinkResourcesPersonalFinanceTools = - publicRoutes.resources.subRoutes.personalFinanceTools.routerLink; - public tags: string[]; - - public constructor( - private dataService: DataService, - private route: ActivatedRoute - ) {} - - public ngOnInit() { +export class GfProductPageComponent { + protected readonly price = computed(() => { const { subscriptionOffer } = this.dataService.fetchInfo(); + return subscriptionOffer?.price; + }); - this.price = subscriptionOffer?.price; + protected readonly product1 = computed(() => ({ + founded: 2021, + hasFreePlan: true, + hasSelfHostingAbility: true, + isOpenSource: true, + key: 'ghostfolio', + languages: [ + 'Chinese (简体中文)', + 'Deutsch', + 'English', + 'Español', + 'Français', + 'Italiano', + 'Korean (한국어)', + 'Nederlands', + 'Português', + 'Türkçe' + ], + name: 'Ghostfolio', + origin: getCountryName({ code: 'CH' }), + regions: [$localize`Global`], + slogan: 'Open Source Wealth Management', + useAnonymously: true + })); - this.product1 = { - founded: 2021, - hasFreePlan: true, - hasSelfHostingAbility: true, - isOpenSource: true, - key: 'ghostfolio', - languages: [ - 'Chinese (简体中文)', - 'Deutsch', - 'English', - 'Español', - 'Français', - 'Italiano', - 'Korean (한국어)', - 'Nederlands', - 'Português', - 'Türkçe' - ], - name: 'Ghostfolio', - origin: getCountryName({ code: 'CH' }), - regions: [$localize`Global`], - slogan: 'Open Source Wealth Management', - useAnonymously: true - }; + protected readonly product2 = computed(() => { + const product = personalFinanceTools.find(({ key }) => { + return key === this.route.snapshot.data['key']; + }); - this.product2 = { - ...personalFinanceTools.find(({ key }) => { - return key === this.route.snapshot.data['key']; - }) + const mappedProduct = { + key: product?.key ?? '', + name: product?.name ?? '', + ...product }; - if (this.product2.origin) { - this.product2.origin = getCountryName({ code: this.product2.origin }); + if (mappedProduct.origin) { + mappedProduct.origin = getCountryName({ code: mappedProduct.origin }); } - if (this.product2.regions) { - this.product2.regions = this.product2.regions.map((region) => { + if (mappedProduct.regions) { + mappedProduct.regions = mappedProduct.regions.map((region) => { return translate(region); }); } - this.tags = [ - this.product1.name, - this.product1.origin, - this.product2.name, - this.product2.origin, + return mappedProduct; + }); + + protected readonly routerLinkAbout = publicRoutes.about.routerLink; + protected readonly routerLinkFeatures = publicRoutes.features.routerLink; + protected readonly routerLinkResourcesPersonalFinanceTools = + publicRoutes.resources.subRoutes.personalFinanceTools.routerLink; + + protected readonly tags = computed(() => { + const product1 = this.product1(); + const product2 = this.product2(); + + return [ + product1.name, + product1.origin, + product2.name, + product2.origin, $localize`Alternative`, $localize`App`, $localize`Budgeting`, @@ -103,11 +111,14 @@ export class GfProductPageComponent implements OnInit { $localize`Wealth Management`, `WealthTech` ] - .filter((item) => { + .filter((item): item is string => { return !!item; }) .sort((a, b) => { return a.localeCompare(b, undefined, { sensitivity: 'base' }); }); - } + }); + + private readonly dataService = inject(DataService); + private readonly route = inject(ActivatedRoute); } diff --git a/apps/client/src/app/pages/resources/personal-finance-tools/product-page.html b/apps/client/src/app/pages/resources/personal-finance-tools/product-page.html index a71ca0038..d86d88621 100644 --- a/apps/client/src/app/pages/resources/personal-finance-tools/product-page.html +++ b/apps/client/src/app/pages/resources/personal-finance-tools/product-page.html @@ -6,10 +6,10 @@

Ghostfolio: The Open Source Alternative to {{ product2.name }} + > {{ product2().name }}

- @if (product2.isArchived) { + @if (product2().isArchived) {
This page has been archived.
@@ -17,7 +17,7 @@

Are you looking for an open source alternative to - {{ product2.name }}? + {{ product2().name }}? Ghostfolio is a powerful portfolio management tool that provides individuals with a comprehensive platform to track, analyze, and optimize their @@ -31,7 +31,7 @@

Ghostfolio is an open source software (OSS), providing a - cost-effective alternative to {{ product2.name }} making it + cost-effective alternative to {{ product2().name }} making it particularly suitable for individuals on a tight budget, such as those

Let’s dive deeper into the detailed Ghostfolio vs - {{ product2.name }} comparison table below to gain a thorough + {{ product2().name }} comparison table below to gain a thorough understanding of how Ghostfolio positions itself relative to - {{ product2.name }}. We will explore various aspects such as + {{ product2().name }}. We will explore various aspects such as features, data privacy, pricing, and more, allowing you to make a well-informed choice for your personal requirements.

@@ -54,7 +54,7 @@ Ghostfolio vs {{ - product2.name + product2().name }} comparison table @@ -63,31 +63,31 @@ Ghostfolio - {{ product2.name }} + {{ product2().name }} - {{ product1.slogan }} - {{ product2.slogan }} + {{ product1().slogan }} + {{ product2().slogan }} Founded - {{ product1.founded }} - {{ product2.founded }} + {{ product1().founded }} + {{ product2().founded }} Origin - {{ product1.origin }} - {{ product2.origin }} + {{ product1().origin }} + {{ product2().origin }} Region @for ( - region of product1.regions; + region of product1().regions; track region; let isLast = $last ) { @@ -96,7 +96,7 @@ @for ( - region of product2.regions; + region of product2().regions; track region; let isLast = $last ) { @@ -110,7 +110,7 @@ @for ( - language of product1.languages; + language of product1().languages; track language; let isLast = $last ) { @@ -119,7 +119,7 @@ @for ( - language of product2.languages; + language of product2().languages; track language; let isLast = $last ) { @@ -132,35 +132,35 @@ Open Source Software - @if (product1.isOpenSource) { + @if (product1().isOpenSource) { ✅ Yes } @else { ❌ No } - @if (product2.isOpenSource) { + @if (product2().isOpenSource) { ✅ Yes } @else { ❌ No } @@ -171,35 +171,35 @@ Self-Hosting - @if (product1.hasSelfHostingAbility === true) { + @if (product1().hasSelfHostingAbility === true) { ✅ Yes - } @else if (product1.hasSelfHostingAbility === false) { + } @else if (product1().hasSelfHostingAbility === false) { ❌ No } - @if (product2.hasSelfHostingAbility === true) { + @if (product2().hasSelfHostingAbility === true) { ✅ Yes - } @else if (product2.hasSelfHostingAbility === false) { + } @else if (product2().hasSelfHostingAbility === false) { ❌ No } @@ -210,35 +210,35 @@ Use anonymously - @if (product1.useAnonymously === true) { + @if (product1().useAnonymously === true) { ✅ Yes - } @else if (product1.useAnonymously === false) { + } @else if (product1().useAnonymously === false) { ❌ No } - @if (product2.useAnonymously === true) { + @if (product2().useAnonymously === true) { ✅ Yes - } @else if (product2.useAnonymously === false) { + } @else if (product2().useAnonymously === false) { ❌ No } @@ -249,35 +249,35 @@ Free Plan - @if (product1.hasFreePlan === true) { + @if (product1().hasFreePlan === true) { ✅ Yes - } @else if (product1.hasFreePlan === false) { + } @else if (product1().hasFreePlan === false) { ❌ No } - @if (product2.hasFreePlan === true) { + @if (product2().hasFreePlan === true) { ✅ Yes - } @else if (product2.hasFreePlan === false) { + } @else if (product2().hasFreePlan === false) { ❌ No } @@ -286,22 +286,22 @@ Pricing - Starting from ${{ price }} / + Starting from ${{ price() }} / year - @if (product2.pricingPerYear) { + @if (product2().pricingPerYear) { Starting from - {{ product2.pricingPerYear }} / + {{ product2().pricingPerYear }} / year } - @if (product1.note || product2.note) { + @if (product1().note || product2().note) { Notes - {{ product1.note }} - {{ product2.note }} + {{ product1().note }} + {{ product2().note }} } @@ -310,9 +310,9 @@

Please note that the information provided in the Ghostfolio vs - {{ product2.name }} comparison table is based on our independent + {{ product2().name }} comparison table is based on our independent research and analysis. This website is not affiliated with - {{ product2.name }} or any other product mentioned in the + {{ product2().name }} or any other product mentioned in the comparison. As the landscape of personal finance tools evolves, it is essential to verify any specific details or changes directly from the respective product page. Data needs a refresh? Help us maintain @@ -337,7 +337,7 @@

    - @for (tag of tags; track tag) { + @for (tag of tags(); track tag) {
  • {{ tag }}
  • @@ -355,7 +355,7 @@ aria-current="page" class="active breadcrumb-item text-truncate" > - Ghostfolio vs {{ product2.name }} + Ghostfolio vs {{ product2().name }}