Browse Source

Feature/add previous and next navigation to personal finance tools (#7899)

Add previous and next navigation
pull/7898/head^2
Thomas Kaul 1 week ago
committed by GitHub
parent
commit
71db6efe07
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 5
      apps/client/src/app/pages/resources/personal-finance-tools/personal-finance-tools-page.component.ts
  2. 33
      apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts
  3. 29
      apps/client/src/app/pages/resources/personal-finance-tools/product-page.html
  4. 20
      libs/common/src/lib/personal-finance-tools.ts

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

@ -23,9 +23,8 @@ export class PersonalFinanceToolsPageComponent {
public pathResources = publicRoutes.resources.path; public pathResources = publicRoutes.resources.path;
public pathPersonalFinanceTools = public pathPersonalFinanceTools =
publicRoutes.resources.subRoutes.personalFinanceTools.path; publicRoutes.resources.subRoutes.personalFinanceTools.path;
public personalFinanceTools = personalFinanceTools.sort((a, b) => {
return a.name.localeCompare(b.name, undefined, { sensitivity: 'base' }); public personalFinanceTools = personalFinanceTools;
});
public routerLinkAbout = publicRoutes.about.routerLink; public routerLinkAbout = publicRoutes.about.routerLink;
public constructor() { public constructor() {

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

@ -1,4 +1,5 @@
import { getCountryName } from '@ghostfolio/common/helper'; import { getCountryName } from '@ghostfolio/common/helper';
import { Product } from '@ghostfolio/common/interfaces';
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 { translate } from '@ghostfolio/ui/i18n'; import { translate } from '@ghostfolio/ui/i18n';
@ -91,9 +92,20 @@ export class GfProductPageComponent {
protected readonly routerLinkAbout = publicRoutes.about.routerLink; protected readonly routerLinkAbout = publicRoutes.about.routerLink;
protected readonly routerLinkFeatures = publicRoutes.features.routerLink; protected readonly routerLinkFeatures = publicRoutes.features.routerLink;
protected readonly routerLinkResourcesPersonalFinanceTools = protected readonly routerLinkResourcesPersonalFinanceTools =
publicRoutes.resources.subRoutes.personalFinanceTools.routerLink; publicRoutes.resources.subRoutes.personalFinanceTools.routerLink;
protected readonly titlePrefix = $localize`The Open Source Alternative to`;
protected readonly nextProduct = computed(() => {
return personalFinanceTools[this.getCurrentProductIndex() + 1];
});
protected readonly previousProduct = computed(() => {
return personalFinanceTools[this.getCurrentProductIndex() - 1];
});
protected readonly tags = computed<string[]>(() => { protected readonly tags = computed<string[]>(() => {
const product1 = this.product1(); const product1 = this.product1();
const product2 = this.product2(); const product2 = this.product2();
@ -139,6 +151,27 @@ export class GfProductPageComponent {
private readonly dataService = inject(DataService); private readonly dataService = inject(DataService);
private readonly route = inject(ActivatedRoute); private readonly route = inject(ActivatedRoute);
protected getProductRouterLink(product?: Product) {
if (!product) {
return this.routerLinkResourcesPersonalFinanceTools;
}
return [
...this.routerLinkResourcesPersonalFinanceTools,
`${publicRoutes.resources.subRoutes.personalFinanceTools.subRoutes.product.path}-${product.alias ?? product.key}`
];
}
protected getProductTitle(product?: Product) {
return `Ghostfolio: ${this.titlePrefix} ${product?.name ?? ''}`;
}
private getCurrentProductIndex() {
return personalFinanceTools.findIndex(({ key }) => {
return key === this.product2().key;
});
}
private getSortedTranslations(values?: string[]) { private getSortedTranslations(values?: string[]) {
return values return values
?.map((value) => { ?.map((value) => {

29
apps/client/src/app/pages/resources/personal-finance-tools/product-page.html

@ -4,9 +4,8 @@
<article> <article>
<div class="mb-4 text-center"> <div class="mb-4 text-center">
<h1 class="mb-1"> <h1 class="mb-1">
<strong>Ghostfolio</strong>: <strong>Ghostfolio</strong>: {{ titlePrefix }}
<ng-container i18n>The Open Source Alternative to</ng-container <strong>{{ product2().name }}</strong>
>&nbsp;<strong>{{ product2().name }}</strong>
</h1> </h1>
</div> </div>
@if (product2().isArchived) { @if (product2().isArchived) {
@ -379,7 +378,7 @@
> >
</div> </div>
</section> </section>
<section class="mb-4"> <section>
<ul class="list-inline"> <ul class="list-inline">
@for (tag of tags(); track tag) { @for (tag of tags(); track tag) {
<li class="list-inline-item"> <li class="list-inline-item">
@ -388,9 +387,29 @@
} }
</ul> </ul>
</section> </section>
<nav class="d-flex justify-content-between my-5">
@if (previousProduct()) {
<a
class="font-weight-light link-primary text-decoration-none small"
[routerLink]="getProductRouterLink(previousProduct())"
[title]="getProductTitle(previousProduct())"
>← <span i18n>Previous</span></a
>
} @else {
<span></span>
}
@if (nextProduct()) {
<a
class="font-weight-light link-primary text-decoration-none small"
[routerLink]="getProductRouterLink(nextProduct())"
[title]="getProductTitle(nextProduct())"
><span i18n>Next</span> →</a
>
}
</nav>
<nav aria-label="breadcrumb"> <nav aria-label="breadcrumb">
<ol class="breadcrumb"> <ol class="breadcrumb">
<li class="breadcrumb-item"> <li class="breadcrumb-item text-truncate">
<a i18n [routerLink]="routerLinkResourcesPersonalFinanceTools" <a i18n [routerLink]="routerLinkResourcesPersonalFinanceTools"
>Personal Finance Tools</a >Personal Finance Tools</a
> >

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

@ -1,6 +1,7 @@
import { Product } from '@ghostfolio/common/interfaces'; import { Product } from '@ghostfolio/common/interfaces';
export const personalFinanceTools: Product[] = [ export const personalFinanceTools: Product[] = (
[
{ {
categories: ['BUDGETING'], categories: ['BUDGETING'],
hasFreePlan: true, hasFreePlan: true,
@ -215,7 +216,8 @@ export const personalFinanceTools: Product[] = [
isArchived: true, isArchived: true,
key: 'basil-finance', key: 'basil-finance',
name: 'Basil Finance', name: 'Basil Finance',
slogan: 'The ultimate solution for tracking and managing your investments', slogan:
'The ultimate solution for tracking and managing your investments',
url: 'https://basil.fi' url: 'https://basil.fi'
}, },
{ {
@ -1536,7 +1538,8 @@ export const personalFinanceTools: Product[] = [
name: 'My Stocks Portfolio & Market', name: 'My Stocks Portfolio & Market',
platforms: ['ANDROID', 'IOS'], platforms: ['ANDROID', 'IOS'],
pricingPerYear: '$49.99', pricingPerYear: '$49.99',
slogan: 'Keep track of and visualize your investments throughout your day', slogan:
'Keep track of and visualize your investments throughout your day',
url: 'https://peeksoft.co' url: 'https://peeksoft.co'
}, },
{ {
@ -1607,7 +1610,8 @@ export const personalFinanceTools: Product[] = [
origin: 'CA', origin: 'CA',
platforms: ['ANDROID', 'IOS', 'WEB'], platforms: ['ANDROID', 'IOS', 'WEB'],
pricingPerYear: '$75', pricingPerYear: '$75',
slogan: 'Everything you own. Everything you owe. Everything in one place.', slogan:
'Everything you own. Everything you owe. Everything in one place.',
url: 'https://neontra.com' url: 'https://neontra.com'
}, },
{ {
@ -2011,7 +2015,8 @@ export const personalFinanceTools: Product[] = [
key: 'sharescope', key: 'sharescope',
name: 'ShareScope', name: 'ShareScope',
origin: 'GB', origin: 'GB',
slogan: 'Award-winning investment, trading and portfolio management tools', slogan:
'Award-winning investment, trading and portfolio management tools',
url: 'https://www.sharescope.co.uk' url: 'https://www.sharescope.co.uk'
}, },
{ {
@ -2718,4 +2723,7 @@ export const personalFinanceTools: Product[] = [
slogan: 'Your solution for investing success', slogan: 'Your solution for investing success',
url: 'https://ziggma.com' url: 'https://ziggma.com'
} }
]; ] satisfies Product[]
).sort((a, b) => {
return a.name.localeCompare(b.name, undefined, { sensitivity: 'base' });
});

Loading…
Cancel
Save