Browse Source

Bugfix/fix page navigation (#4711)

* Fix page navigation and use paths references
pull/4732/head
Kenrick Tandrian 1 month ago
committed by GitHub
parent
commit
4bffb3107d
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 44
      apps/client/src/app/app-routing.module.ts
  2. 33
      apps/client/src/app/app.component.ts
  3. 3
      apps/client/src/app/components/access-table/access-table.component.ts
  4. 5
      apps/client/src/app/components/account-detail-dialog/account-detail-dialog.component.ts
  5. 5
      apps/client/src/app/components/admin-settings/admin-settings.component.ts
  6. 23
      apps/client/src/app/components/header/header.component.ts
  7. 5
      apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts
  8. 4
      apps/client/src/app/components/subscription-interstitial-dialog/subscription-interstitial-dialog.component.ts
  9. 3
      apps/client/src/app/components/user-account-membership/user-account-membership.component.ts
  10. 37
      apps/client/src/app/core/auth.guard.ts
  11. 5
      apps/client/src/app/core/http-response.interceptor.ts
  12. 12
      apps/client/src/app/core/paths.ts
  13. 6
      apps/client/src/app/pages/about/about-page-routing.module.ts
  14. 13
      apps/client/src/app/pages/about/about-page.component.ts
  15. 5
      apps/client/src/app/pages/about/overview/about-overview-page.component.ts
  16. 9
      apps/client/src/app/pages/admin/admin-page-routing.module.ts
  17. 11
      apps/client/src/app/pages/admin/admin-page.component.ts
  18. 6
      apps/client/src/app/pages/blog/2021/07/hallo-ghostfolio/hallo-ghostfolio-page.component.ts
  19. 6
      apps/client/src/app/pages/blog/2021/07/hello-ghostfolio/hello-ghostfolio-page.component.ts
  20. 4
      apps/client/src/app/pages/blog/2022/01/first-months-in-open-source/first-months-in-open-source-page.component.ts
  21. 4
      apps/client/src/app/pages/blog/2022/07/how-do-i-get-my-finances-in-order/how-do-i-get-my-finances-in-order-page.component.ts
  22. 6
      apps/client/src/app/pages/blog/2022/08/500-stars-on-github/500-stars-on-github-page.component.ts
  23. 5
      apps/client/src/app/pages/blog/2022/11/black-friday-2022/black-friday-2022-page.component.ts
  24. 6
      apps/client/src/app/pages/blog/2023/03/1000-stars-on-github/1000-stars-on-github-page.component.ts
  25. 6
      apps/client/src/app/pages/blog/2023/05/unlock-your-financial-potential-with-ghostfolio/unlock-your-financial-potential-with-ghostfolio-page.component.ts
  26. 4
      apps/client/src/app/pages/blog/2023/07/exploring-the-path-to-fire/exploring-the-path-to-fire-page.component.ts
  27. 7
      apps/client/src/app/pages/blog/2023/08/ghostfolio-joins-oss-friends/ghostfolio-joins-oss-friends-page.component.ts
  28. 13
      apps/client/src/app/pages/blog/2023/09/ghostfolio-2/ghostfolio-2-page.component.ts
  29. 4
      apps/client/src/app/pages/blog/2023/09/hacktoberfest-2023/hacktoberfest-2023-page.component.ts
  30. 5
      apps/client/src/app/pages/blog/2023/11/black-week-2023/black-week-2023-page.component.ts
  31. 6
      apps/client/src/app/pages/blog/2023/11/hacktoberfest-2023-debriefing/hacktoberfest-2023-debriefing-page.component.ts
  32. 4
      apps/client/src/app/pages/blog/2024/09/hacktoberfest-2024/hacktoberfest-2024-page.component.ts
  33. 5
      apps/client/src/app/pages/blog/2024/11/black-weeks-2024/black-weeks-2024-page.component.ts
  34. 5
      apps/client/src/app/pages/faq/faq-page-routing.module.ts
  35. 7
      apps/client/src/app/pages/faq/faq-page.component.ts
  36. 7
      apps/client/src/app/pages/faq/overview/faq-overview-page.component.ts
  37. 9
      apps/client/src/app/pages/faq/saas/saas-page.component.ts
  38. 6
      apps/client/src/app/pages/faq/self-hosting/self-hosting-page.component.ts
  39. 5
      apps/client/src/app/pages/features/features-page.component.ts
  40. 14
      apps/client/src/app/pages/home/home-page-routing.module.ts
  41. 11
      apps/client/src/app/pages/home/home-page.component.ts
  42. 5
      apps/client/src/app/pages/landing/landing-page.component.ts
  43. 9
      apps/client/src/app/pages/portfolio/portfolio-page-routing.module.ts
  44. 11
      apps/client/src/app/pages/portfolio/portfolio-page.component.ts
  45. 5
      apps/client/src/app/pages/pricing/pricing-page.component.ts
  46. 5
      apps/client/src/app/pages/register/show-access-token-dialog/show-access-token-dialog.component.ts
  47. 5
      apps/client/src/app/pages/resources/glossary/resources-glossary.component.ts
  48. 19
      apps/client/src/app/pages/resources/overview/resources-overview.component.ts
  49. 3
      apps/client/src/app/pages/resources/personal-finance-tools/personal-finance-tools-page-routing.module.ts
  50. 7
      apps/client/src/app/pages/resources/personal-finance-tools/personal-finance-tools-page.component.ts
  51. 9
      apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts
  52. 9
      apps/client/src/app/pages/resources/resources-page-routing.module.ts
  53. 8
      apps/client/src/app/pages/resources/resources-page.component.ts
  54. 5
      apps/client/src/app/pages/user-account/user-account-page-routing.module.ts
  55. 7
      apps/client/src/app/pages/user-account/user-account-page.component.ts
  56. 3
      apps/client/src/app/pages/zen/zen-page-routing.module.ts
  57. 5
      apps/client/src/app/pages/zen/zen-page.component.ts
  58. 714
      apps/client/src/locales/messages.ca.xlf
  59. 892
      apps/client/src/locales/messages.de.xlf
  60. 892
      apps/client/src/locales/messages.es.xlf
  61. 892
      apps/client/src/locales/messages.fr.xlf
  62. 892
      apps/client/src/locales/messages.it.xlf
  63. 892
      apps/client/src/locales/messages.nl.xlf
  64. 712
      apps/client/src/locales/messages.pl.xlf
  65. 892
      apps/client/src/locales/messages.pt.xlf
  66. 712
      apps/client/src/locales/messages.tr.xlf
  67. 714
      apps/client/src/locales/messages.uk.xlf
  68. 706
      apps/client/src/locales/messages.xlf
  69. 716
      apps/client/src/locales/messages.zh.xlf
  70. 53
      libs/common/src/lib/paths.ts
  71. 4
      libs/ui/src/lib/membership-card/membership-card.component.ts
  72. 4
      libs/ui/src/lib/premium-indicator/premium-indicator.component.ts

44
apps/client/src/app/app-routing.module.ts

@ -1,6 +1,6 @@
import { AuthGuard } from '@ghostfolio/client/core/auth.guard'; import { AuthGuard } from '@ghostfolio/client/core/auth.guard';
import { paths } from '@ghostfolio/client/core/paths';
import { PageTitleStrategy } from '@ghostfolio/client/services/page-title.strategy'; import { PageTitleStrategy } from '@ghostfolio/client/services/page-title.strategy';
import { paths } from '@ghostfolio/common/paths';
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { RouterModule, Routes, TitleStrategy } from '@angular/router'; import { RouterModule, Routes, TitleStrategy } from '@angular/router';
@ -14,21 +14,21 @@ const routes: Routes = [
import('./pages/about/about-page.module').then((m) => m.AboutPageModule) import('./pages/about/about-page.module').then((m) => m.AboutPageModule)
}, },
{ {
path: 'account', path: paths.account,
loadChildren: () => loadChildren: () =>
import('./pages/user-account/user-account-page.module').then( import('./pages/user-account/user-account-page.module').then(
(m) => m.UserAccountPageModule (m) => m.UserAccountPageModule
) )
}, },
{ {
path: 'accounts', path: paths.accounts,
loadChildren: () => loadChildren: () =>
import('./pages/accounts/accounts-page.module').then( import('./pages/accounts/accounts-page.module').then(
(m) => m.AccountsPageModule (m) => m.AccountsPageModule
) )
}, },
{ {
path: 'admin', path: paths.admin,
loadChildren: () => loadChildren: () =>
import('./pages/admin/admin-page.module').then((m) => m.AdminPageModule) import('./pages/admin/admin-page.module').then((m) => m.AdminPageModule)
}, },
@ -38,16 +38,16 @@ const routes: Routes = [
import('./pages/api/api-page.component').then( import('./pages/api/api-page.component').then(
(c) => c.GfApiPageComponent (c) => c.GfApiPageComponent
), ),
path: 'api', path: paths.api,
title: 'Ghostfolio API' title: 'Ghostfolio API'
}, },
{ {
path: 'auth', path: paths.auth,
loadChildren: () => loadChildren: () =>
import('./pages/auth/auth-page.module').then((m) => m.AuthPageModule) import('./pages/auth/auth-page.module').then((m) => m.AuthPageModule)
}, },
{ {
path: 'blog', path: paths.blog,
loadChildren: () => loadChildren: () =>
import('./pages/blog/blog-page.module').then((m) => m.BlogPageModule) import('./pages/blog/blog-page.module').then((m) => m.BlogPageModule)
}, },
@ -57,7 +57,7 @@ const routes: Routes = [
import('./pages/demo/demo-page.component').then( import('./pages/demo/demo-page.component').then(
(c) => c.GfDemoPageComponent (c) => c.GfDemoPageComponent
), ),
path: 'demo' path: paths.demo
}, },
{ {
path: paths.faq, path: paths.faq,
@ -74,7 +74,7 @@ const routes: Routes = [
title: $localize`Features` title: $localize`Features`
}, },
{ {
path: 'home', path: paths.home,
loadChildren: () => loadChildren: () =>
import('./pages/home/home-page.module').then((m) => m.HomePageModule) import('./pages/home/home-page.module').then((m) => m.HomePageModule)
}, },
@ -84,7 +84,7 @@ const routes: Routes = [
import('./pages/i18n/i18n-page.component').then( import('./pages/i18n/i18n-page.component').then(
(c) => c.GfI18nPageComponent (c) => c.GfI18nPageComponent
), ),
path: 'i18n', path: paths.i18n,
title: $localize`Internationalization` title: $localize`Internationalization`
}, },
{ {
@ -95,19 +95,12 @@ const routes: Routes = [
) )
}, },
{ {
path: 'open', path: paths.open,
loadChildren: () => loadChildren: () =>
import('./pages/open/open-page.module').then((m) => m.OpenPageModule) import('./pages/open/open-page.module').then((m) => m.OpenPageModule)
}, },
{ {
path: 'p', path: paths.portfolio,
loadChildren: () =>
import('./pages/public/public-page.module').then(
(m) => m.PublicPageModule
)
},
{
path: 'portfolio',
loadChildren: () => loadChildren: () =>
import('./pages/portfolio/portfolio-page.module').then( import('./pages/portfolio/portfolio-page.module').then(
(m) => m.PortfolioPageModule (m) => m.PortfolioPageModule
@ -120,6 +113,13 @@ const routes: Routes = [
(m) => m.PricingPageModule (m) => m.PricingPageModule
) )
}, },
{
path: paths.public,
loadChildren: () =>
import('./pages/public/public-page.module').then(
(m) => m.PublicPageModule
)
},
{ {
path: paths.register, path: paths.register,
loadChildren: () => loadChildren: () =>
@ -135,7 +135,7 @@ const routes: Routes = [
) )
}, },
{ {
path: 'start', path: paths.start,
loadChildren: () => loadChildren: () =>
import('./pages/landing/landing-page.module').then( import('./pages/landing/landing-page.module').then(
(m) => m.LandingPageModule (m) => m.LandingPageModule
@ -146,11 +146,11 @@ const routes: Routes = [
import('./pages/webauthn/webauthn-page.component').then( import('./pages/webauthn/webauthn-page.component').then(
(c) => c.GfWebauthnPageComponent (c) => c.GfWebauthnPageComponent
), ),
path: 'webauthn', path: paths.webauthn,
title: $localize`Sign in` title: $localize`Sign in`
}, },
{ {
path: 'zen', path: paths.zen,
loadChildren: () => loadChildren: () =>
import('./pages/zen/zen-page.module').then((m) => m.ZenPageModule) import('./pages/zen/zen-page.module').then((m) => m.ZenPageModule)
}, },

33
apps/client/src/app/app.component.ts

@ -2,6 +2,7 @@ import { GfHoldingDetailDialogComponent } from '@ghostfolio/client/components/ho
import { HoldingDetailDialogParams } from '@ghostfolio/client/components/holding-detail-dialog/interfaces/interfaces'; import { HoldingDetailDialogParams } from '@ghostfolio/client/components/holding-detail-dialog/interfaces/interfaces';
import { getCssVariable } from '@ghostfolio/common/helper'; import { getCssVariable } from '@ghostfolio/common/helper';
import { InfoItem, User } from '@ghostfolio/common/interfaces'; import { InfoItem, User } from '@ghostfolio/common/interfaces';
import { paths } from '@ghostfolio/common/paths';
import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import { ColorScheme } from '@ghostfolio/common/types'; import { ColorScheme } from '@ghostfolio/common/types';
@ -62,29 +63,23 @@ export class AppComponent implements OnDestroy, OnInit {
public hasTabs = false; public hasTabs = false;
public info: InfoItem; public info: InfoItem;
public pageTitle: string; public pageTitle: string;
public routerLinkAbout = ['/' + $localize`:snake-case:about`]; public routerLinkAbout = ['/' + paths.about];
public routerLinkAboutChangelog = [ public routerLinkAboutChangelog = ['/' + paths.about, paths.changelog];
'/' + $localize`:snake-case:about`, public routerLinkAboutLicense = ['/' + paths.about, paths.license];
'changelog'
];
public routerLinkAboutLicense = [
'/' + $localize`:snake-case:about`,
$localize`:snake-case:license`
];
public routerLinkAboutPrivacyPolicy = [ public routerLinkAboutPrivacyPolicy = [
'/' + $localize`:snake-case:about`, '/' + paths.about,
$localize`:snake-case:privacy-policy` paths.privacyPolicy
]; ];
public routerLinkAboutTermsOfService = [ public routerLinkAboutTermsOfService = [
'/' + $localize`:snake-case:about`, '/' + paths.about,
$localize`:snake-case:terms-of-service` paths.termsOfService
]; ];
public routerLinkFaq = ['/' + $localize`:snake-case:faq`]; public routerLinkFaq = ['/' + paths.faq];
public routerLinkFeatures = ['/' + $localize`:snake-case:features`]; public routerLinkFeatures = ['/' + paths.features];
public routerLinkMarkets = ['/' + $localize`:snake-case:markets`]; public routerLinkMarkets = ['/' + paths.markets];
public routerLinkPricing = ['/' + $localize`:snake-case:pricing`]; public routerLinkPricing = ['/' + paths.pricing];
public routerLinkRegister = ['/' + $localize`:snake-case:register`]; public routerLinkRegister = ['/' + paths.register];
public routerLinkResources = ['/' + $localize`:snake-case:resources`]; public routerLinkResources = ['/' + paths.resources];
public showFooter = false; public showFooter = false;
public user: User; public user: User;

3
apps/client/src/app/components/access-table/access-table.component.ts

@ -2,6 +2,7 @@ import { ConfirmationDialogType } from '@ghostfolio/client/core/notification/con
import { NotificationService } from '@ghostfolio/client/core/notification/notification.service'; import { NotificationService } from '@ghostfolio/client/core/notification/notification.service';
import { DEFAULT_LANGUAGE_CODE } from '@ghostfolio/common/config'; import { DEFAULT_LANGUAGE_CODE } from '@ghostfolio/common/config';
import { Access, User } from '@ghostfolio/common/interfaces'; import { Access, User } from '@ghostfolio/common/interfaces';
import { paths } from '@ghostfolio/common/paths';
import { Clipboard } from '@angular/cdk/clipboard'; import { Clipboard } from '@angular/cdk/clipboard';
import { import {
@ -55,7 +56,7 @@ export class AccessTableComponent implements OnChanges {
public getPublicUrl(aId: string): string { public getPublicUrl(aId: string): string {
const languageCode = this.user?.settings?.language ?? DEFAULT_LANGUAGE_CODE; const languageCode = this.user?.settings?.language ?? DEFAULT_LANGUAGE_CODE;
return `${this.baseUrl}/${languageCode}/p/${aId}`; return `${this.baseUrl}/${languageCode}/${paths.public}/${aId}`;
} }
public onCopyUrlToClipboard(aId: string): void { public onCopyUrlToClipboard(aId: string): void {

5
apps/client/src/app/components/account-detail-dialog/account-detail-dialog.component.ts

@ -9,6 +9,7 @@ import {
PortfolioPosition, PortfolioPosition,
User User
} from '@ghostfolio/common/interfaces'; } from '@ghostfolio/common/interfaces';
import { paths } from '@ghostfolio/common/paths';
import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import { OrderWithAccount } from '@ghostfolio/common/types'; import { OrderWithAccount } from '@ghostfolio/common/types';
@ -92,7 +93,7 @@ export class AccountDetailDialog implements OnDestroy, OnInit {
} }
public onCloneActivity(aActivity: Activity) { public onCloneActivity(aActivity: Activity) {
this.router.navigate(['/portfolio', 'activities'], { this.router.navigate(['/' + paths.portfolio, paths.activities], {
queryParams: { activityId: aActivity.id, createDialog: true } queryParams: { activityId: aActivity.id, createDialog: true }
}); });
@ -151,7 +152,7 @@ export class AccountDetailDialog implements OnDestroy, OnInit {
} }
public onUpdateActivity(aActivity: Activity) { public onUpdateActivity(aActivity: Activity) {
this.router.navigate(['/portfolio', 'activities'], { this.router.navigate(['/' + paths.portfolio, paths.activities], {
queryParams: { activityId: aActivity.id, editDialog: true } queryParams: { activityId: aActivity.id, editDialog: true }
}); });

5
apps/client/src/app/components/admin-settings/admin-settings.component.ts

@ -13,6 +13,7 @@ import {
DataProviderInfo, DataProviderInfo,
User User
} from '@ghostfolio/common/interfaces'; } from '@ghostfolio/common/interfaces';
import { paths } from '@ghostfolio/common/paths';
import { import {
ChangeDetectionStrategy, ChangeDetectionStrategy,
@ -75,9 +76,7 @@ export class AdminSettingsComponent implements OnDestroy, OnInit {
const languageCode = const languageCode =
this.user?.settings?.language ?? DEFAULT_LANGUAGE_CODE; this.user?.settings?.language ?? DEFAULT_LANGUAGE_CODE;
this.pricingUrl = this.pricingUrl = `https://ghostfol.io/${languageCode}/${paths.pricing}`;
`https://ghostfol.io/${languageCode}/` +
$localize`:snake-case:pricing`;
this.changeDetectorRef.markForCheck(); this.changeDetectorRef.markForCheck();
} }

23
apps/client/src/app/components/header/header.component.ts

@ -11,6 +11,7 @@ import {
import { TokenStorageService } from '@ghostfolio/client/services/token-storage.service'; import { TokenStorageService } from '@ghostfolio/client/services/token-storage.service';
import { UserService } from '@ghostfolio/client/services/user/user.service'; import { UserService } from '@ghostfolio/client/services/user/user.service';
import { Filter, InfoItem, User } from '@ghostfolio/common/interfaces'; import { Filter, InfoItem, User } from '@ghostfolio/common/interfaces';
import { paths } from '@ghostfolio/common/paths';
import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import { DateRange } from '@ghostfolio/common/types'; import { DateRange } from '@ghostfolio/common/types';
import { GfAssistantComponent } from '@ghostfolio/ui/assistant/assistant.component'; import { GfAssistantComponent } from '@ghostfolio/ui/assistant/assistant.component';
@ -79,17 +80,17 @@ export class HeaderComponent implements OnChanges {
public hasPermissionToCreateUser: boolean; public hasPermissionToCreateUser: boolean;
public impersonationId: string; public impersonationId: string;
public isMenuOpen: boolean; public isMenuOpen: boolean;
public routeAbout = $localize`:snake-case:about`; public routeAbout = paths.about;
public routeFeatures = $localize`:snake-case:features`; public routeFeatures = paths.features;
public routeMarkets = $localize`:snake-case:markets`; public routeMarkets = paths.markets;
public routePricing = $localize`:snake-case:pricing`; public routePricing = paths.pricing;
public routeResources = $localize`:snake-case:resources`; public routeResources = paths.resources;
public routerLinkAbout = ['/' + $localize`:snake-case:about`]; public routerLinkAbout = ['/' + paths.about];
public routerLinkFeatures = ['/' + $localize`:snake-case:features`]; public routerLinkFeatures = ['/' + paths.features];
public routerLinkMarkets = ['/' + $localize`:snake-case:markets`]; public routerLinkMarkets = ['/' + paths.markets];
public routerLinkPricing = ['/' + $localize`:snake-case:pricing`]; public routerLinkPricing = ['/' + paths.pricing];
public routerLinkRegister = ['/' + $localize`:snake-case:register`]; public routerLinkRegister = ['/' + paths.register];
public routerLinkResources = ['/' + $localize`:snake-case:resources`]; public routerLinkResources = ['/' + paths.resources];
private unsubscribeSubject = new Subject<void>(); private unsubscribeSubject = new Subject<void>();

5
apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts

@ -13,6 +13,7 @@ import {
LineChartItem, LineChartItem,
User User
} from '@ghostfolio/common/interfaces'; } from '@ghostfolio/common/interfaces';
import { paths } from '@ghostfolio/common/paths';
import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import { GfActivitiesTableComponent } from '@ghostfolio/ui/activities-table'; import { GfActivitiesTableComponent } from '@ghostfolio/ui/activities-table';
import { GfDataProviderCreditsComponent } from '@ghostfolio/ui/data-provider-credits'; import { GfDataProviderCreditsComponent } from '@ghostfolio/ui/data-provider-credits';
@ -468,7 +469,7 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit {
} }
public onCloneActivity(aActivity: Activity) { public onCloneActivity(aActivity: Activity) {
this.router.navigate(['/portfolio', 'activities'], { this.router.navigate(['/' + paths.portfolio, paths.activities], {
queryParams: { activityId: aActivity.id, createDialog: true } queryParams: { activityId: aActivity.id, createDialog: true }
}); });
@ -510,7 +511,7 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit {
} }
public onUpdateActivity(aActivity: Activity) { public onUpdateActivity(aActivity: Activity) {
this.router.navigate(['/portfolio', 'activities'], { this.router.navigate(['/' + paths.portfolio, paths.activities], {
queryParams: { activityId: aActivity.id, editDialog: true } queryParams: { activityId: aActivity.id, editDialog: true }
}); });

4
apps/client/src/app/components/subscription-interstitial-dialog/subscription-interstitial-dialog.component.ts

@ -1,3 +1,5 @@
import { paths } from '@ghostfolio/common/paths';
import { import {
ChangeDetectionStrategy, ChangeDetectionStrategy,
ChangeDetectorRef, ChangeDetectorRef,
@ -26,7 +28,7 @@ export class SubscriptionInterstitialDialog implements OnInit {
public remainingSkipButtonDelay = public remainingSkipButtonDelay =
SubscriptionInterstitialDialog.SKIP_BUTTON_DELAY_IN_SECONDS; SubscriptionInterstitialDialog.SKIP_BUTTON_DELAY_IN_SECONDS;
public routerLinkPricing = ['/' + $localize`:snake-case:pricing`]; public routerLinkPricing = ['/' + paths.pricing];
public variantIndex: number; public variantIndex: number;
private unsubscribeSubject = new Subject<void>(); private unsubscribeSubject = new Subject<void>();

3
apps/client/src/app/components/user-account-membership/user-account-membership.component.ts

@ -4,6 +4,7 @@ import { DataService } from '@ghostfolio/client/services/data.service';
import { UserService } from '@ghostfolio/client/services/user/user.service'; import { UserService } from '@ghostfolio/client/services/user/user.service';
import { getDateFormatString } from '@ghostfolio/common/helper'; import { getDateFormatString } from '@ghostfolio/common/helper';
import { User } from '@ghostfolio/common/interfaces'; import { User } from '@ghostfolio/common/interfaces';
import { paths } from '@ghostfolio/common/paths';
import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import { import {
@ -36,7 +37,7 @@ export class UserAccountMembershipComponent implements OnDestroy {
public hasPermissionToUpdateUserSettings: boolean; public hasPermissionToUpdateUserSettings: boolean;
public price: number; public price: number;
public priceId: string; public priceId: string;
public routerLinkPricing = ['/' + $localize`:snake-case:pricing`]; public routerLinkPricing = ['/' + paths.pricing];
public trySubscriptionMail = public trySubscriptionMail =
'mailto:hi@ghostfol.io?Subject=Ghostfolio Premium Trial&body=Hello%0D%0DI am interested in Ghostfolio Premium. Can you please send me a coupon code to try it for some time?%0D%0DKind regards'; 'mailto:hi@ghostfol.io?Subject=Ghostfolio Premium Trial&body=Hello%0D%0DI am interested in Ghostfolio Premium. Can you please send me a coupon code to try it for some time?%0D%0DKind regards';
public user: User; public user: User;

37
apps/client/src/app/core/auth.guard.ts

@ -1,6 +1,7 @@
import { DataService } from '@ghostfolio/client/services/data.service'; import { DataService } from '@ghostfolio/client/services/data.service';
import { SettingsStorageService } from '@ghostfolio/client/services/settings-storage.service'; import { SettingsStorageService } from '@ghostfolio/client/services/settings-storage.service';
import { UserService } from '@ghostfolio/client/services/user/user.service'; import { UserService } from '@ghostfolio/client/services/user/user.service';
import { paths } from '@ghostfolio/common/paths';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { import {
@ -11,20 +12,18 @@ import {
import { EMPTY } from 'rxjs'; import { EMPTY } from 'rxjs';
import { catchError } from 'rxjs/operators'; import { catchError } from 'rxjs/operators';
import { paths } from './paths';
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class AuthGuard { export class AuthGuard {
private static PUBLIC_PAGE_ROUTES = [ private static PUBLIC_PAGE_ROUTES = [
`/${paths.about}`, `/${paths.about}`,
'/blog', `/${paths.blog}`,
'/demo', `/${paths.demo}`,
`/${paths.faq}`, `/${paths.faq}`,
`/${paths.features}`, `/${paths.features}`,
`/${paths.markets}`, `/${paths.markets}`,
'/open', `/${paths.open}`,
'/p',
`/${paths.pricing}`, `/${paths.pricing}`,
`/${paths.public}`,
`/${paths.register}`, `/${paths.register}`,
`/${paths.resources}` `/${paths.resources}`
]; ];
@ -49,21 +48,21 @@ export class AuthGuard {
.pipe( .pipe(
catchError(() => { catchError(() => {
if (utmSource === 'ios') { if (utmSource === 'ios') {
this.router.navigate(['/demo']); this.router.navigate(['/' + paths.demo]);
resolve(false); resolve(false);
} else if (utmSource === 'trusted-web-activity') { } else if (utmSource === 'trusted-web-activity') {
this.router.navigate(['/' + $localize`register`]); this.router.navigate(['/' + paths.register]);
resolve(false); resolve(false);
} else if ( } else if (
AuthGuard.PUBLIC_PAGE_ROUTES.filter((publicPageRoute) => { AuthGuard.PUBLIC_PAGE_ROUTES.some((publicPageRoute) => {
const [, url] = state.url.split('/'); const [, url] = decodeURIComponent(state.url).split('/');
return `/${url}` === publicPageRoute; return `/${url}` === publicPageRoute;
})?.length > 0 })
) { ) {
resolve(true); resolve(true);
return EMPTY; return EMPTY;
} else if (state.url !== '/start') { } else if (state.url !== '/start') {
this.router.navigate(['/start']); this.router.navigate(['/' + paths.start]);
resolve(false); resolve(false);
return EMPTY; return EMPTY;
} }
@ -89,26 +88,26 @@ export class AuthGuard {
resolve(true); resolve(true);
return; return;
} else if ( } else if (
state.url.startsWith('/home') && state.url.startsWith(`/${paths.home}`) &&
user.settings.viewMode === 'ZEN' user.settings.viewMode === 'ZEN'
) { ) {
this.router.navigate(['/zen']); this.router.navigate(['/' + paths.zen]);
resolve(false); resolve(false);
return; return;
} else if (state.url.startsWith('/start')) { } else if (state.url.startsWith(`/${paths.start}`)) {
if (user.settings.viewMode === 'ZEN') { if (user.settings.viewMode === 'ZEN') {
this.router.navigate(['/zen']); this.router.navigate(['/' + paths.zen]);
} else { } else {
this.router.navigate(['/home']); this.router.navigate(['/' + paths.home]);
} }
resolve(false); resolve(false);
return; return;
} else if ( } else if (
state.url.startsWith('/zen') && state.url.startsWith(`/${paths.zen}`) &&
user.settings.viewMode === 'DEFAULT' user.settings.viewMode === 'DEFAULT'
) { ) {
this.router.navigate(['/home']); this.router.navigate(['/' + paths.home]);
resolve(false); resolve(false);
return; return;
} }

5
apps/client/src/app/core/http-response.interceptor.ts

@ -2,6 +2,7 @@ import { DataService } from '@ghostfolio/client/services/data.service';
import { TokenStorageService } from '@ghostfolio/client/services/token-storage.service'; import { TokenStorageService } from '@ghostfolio/client/services/token-storage.service';
import { WebAuthnService } from '@ghostfolio/client/services/web-authn.service'; import { WebAuthnService } from '@ghostfolio/client/services/web-authn.service';
import { InfoItem } from '@ghostfolio/common/interfaces'; import { InfoItem } from '@ghostfolio/common/interfaces';
import { paths } from '@ghostfolio/common/paths';
import { import {
HTTP_INTERCEPTORS, HTTP_INTERCEPTORS,
@ -74,7 +75,7 @@ export class HttpResponseInterceptor implements HttpInterceptor {
}); });
this.snackBarRef.onAction().subscribe(() => { this.snackBarRef.onAction().subscribe(() => {
this.router.navigate(['/' + $localize`pricing`]); this.router.navigate(['/' + paths.pricing]);
}); });
} }
} else if (error.status === StatusCodes.INTERNAL_SERVER_ERROR) { } else if (error.status === StatusCodes.INTERNAL_SERVER_ERROR) {
@ -110,7 +111,7 @@ export class HttpResponseInterceptor implements HttpInterceptor {
} else if (error.status === StatusCodes.UNAUTHORIZED) { } else if (error.status === StatusCodes.UNAUTHORIZED) {
if (!error.url.includes('/data-providers/ghostfolio/status')) { if (!error.url.includes('/data-providers/ghostfolio/status')) {
if (this.webAuthnService.isEnabled()) { if (this.webAuthnService.isEnabled()) {
this.router.navigate(['/webauthn']); this.router.navigate(['/' + paths.webauthn]);
} else { } else {
this.tokenStorageService.signOut(); this.tokenStorageService.signOut();
} }

12
apps/client/src/app/core/paths.ts

@ -1,12 +0,0 @@
export const paths = {
about: $localize`about`,
faq: $localize`faq`,
features: $localize`features`,
license: $localize`license`,
markets: $localize`markets`,
pricing: $localize`pricing`,
privacyPolicy: $localize`privacy-policy`,
register: $localize`register`,
resources: $localize`resources`,
termsOfService: $localize`terms-of-service`
};

6
apps/client/src/app/pages/about/about-page-routing.module.ts

@ -1,5 +1,5 @@
import { AuthGuard } from '@ghostfolio/client/core/auth.guard'; import { AuthGuard } from '@ghostfolio/client/core/auth.guard';
import { paths } from '@ghostfolio/client/core/paths'; import { paths } from '@ghostfolio/common/paths';
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router';
@ -18,7 +18,7 @@ const routes: Routes = [
) )
}, },
{ {
path: 'changelog', path: paths.changelog,
loadChildren: () => loadChildren: () =>
import('./changelog/changelog-page.module').then( import('./changelog/changelog-page.module').then(
(m) => m.ChangelogPageModule (m) => m.ChangelogPageModule
@ -32,7 +32,7 @@ const routes: Routes = [
) )
}, },
{ {
path: 'oss-friends', path: paths.ossFriends,
loadChildren: () => loadChildren: () =>
import('./oss-friends/oss-friends-page.module').then( import('./oss-friends/oss-friends-page.module').then(
(m) => m.OpenSourceSoftwareFriendsPageModule (m) => m.OpenSourceSoftwareFriendsPageModule

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

@ -1,6 +1,7 @@
import { DataService } from '@ghostfolio/client/services/data.service'; import { DataService } from '@ghostfolio/client/services/data.service';
import { UserService } from '@ghostfolio/client/services/user/user.service'; import { UserService } from '@ghostfolio/client/services/user/user.service';
import { TabConfiguration, User } from '@ghostfolio/common/interfaces'; import { TabConfiguration, User } from '@ghostfolio/common/interfaces';
import { paths } from '@ghostfolio/common/paths';
import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
@ -43,17 +44,17 @@ export class AboutPageComponent implements OnDestroy, OnInit {
{ {
iconName: 'information-circle-outline', iconName: 'information-circle-outline',
label: $localize`About`, label: $localize`About`,
path: ['/' + $localize`about`] path: ['/' + paths.about]
}, },
{ {
iconName: 'sparkles-outline', iconName: 'sparkles-outline',
label: $localize`Changelog`, label: $localize`Changelog`,
path: ['/' + $localize`about`, 'changelog'] path: ['/' + paths.about, paths.changelog]
}, },
{ {
iconName: 'ribbon-outline', iconName: 'ribbon-outline',
label: $localize`License`, label: $localize`License`,
path: ['/' + $localize`about`, $localize`license`], path: ['/' + paths.about, paths.license],
showCondition: !this.hasPermissionForSubscription showCondition: !this.hasPermissionForSubscription
} }
]; ];
@ -62,14 +63,14 @@ export class AboutPageComponent implements OnDestroy, OnInit {
this.tabs.push({ this.tabs.push({
iconName: 'shield-checkmark-outline', iconName: 'shield-checkmark-outline',
label: $localize`Privacy Policy`, label: $localize`Privacy Policy`,
path: ['/' + $localize`about`, $localize`privacy-policy`], path: ['/' + paths.about, paths.privacyPolicy],
showCondition: this.hasPermissionForSubscription showCondition: this.hasPermissionForSubscription
}); });
this.tabs.push({ this.tabs.push({
iconName: 'document-text-outline', iconName: 'document-text-outline',
label: $localize`Terms of Service`, label: $localize`Terms of Service`,
path: ['/' + $localize`about`, $localize`terms-of-service`], path: ['/' + paths.about, paths.termsOfService],
showCondition: this.hasPermissionForSubscription showCondition: this.hasPermissionForSubscription
}); });
@ -81,7 +82,7 @@ export class AboutPageComponent implements OnDestroy, OnInit {
this.tabs.push({ this.tabs.push({
iconName: 'happy-outline', iconName: 'happy-outline',
label: 'OSS Friends', label: 'OSS Friends',
path: ['/' + $localize`about`, 'oss-friends'] path: ['/' + paths.about, paths.ossFriends]
}); });
}); });
} }

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

@ -1,6 +1,7 @@
import { DataService } from '@ghostfolio/client/services/data.service'; import { DataService } from '@ghostfolio/client/services/data.service';
import { UserService } from '@ghostfolio/client/services/user/user.service'; import { UserService } from '@ghostfolio/client/services/user/user.service';
import { User } from '@ghostfolio/common/interfaces'; import { User } from '@ghostfolio/common/interfaces';
import { paths } from '@ghostfolio/common/paths';
import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
@ -17,8 +18,8 @@ export class AboutOverviewPageComponent implements OnDestroy, OnInit {
public hasPermissionForStatistics: boolean; public hasPermissionForStatistics: boolean;
public hasPermissionForSubscription: boolean; public hasPermissionForSubscription: boolean;
public isLoggedIn: boolean; public isLoggedIn: boolean;
public routerLinkFaq = ['/' + $localize`:snake-case:faq`]; public routerLinkFaq = ['/' + paths.faq];
public routerLinkFeatures = ['/' + $localize`:snake-case:features`]; public routerLinkFeatures = ['/' + paths.features];
public user: User; public user: User;
private unsubscribeSubject = new Subject<void>(); private unsubscribeSubject = new Subject<void>();

9
apps/client/src/app/pages/admin/admin-page-routing.module.ts

@ -4,6 +4,7 @@ import { AdminOverviewComponent } from '@ghostfolio/client/components/admin-over
import { AdminSettingsComponent } from '@ghostfolio/client/components/admin-settings/admin-settings.component'; import { AdminSettingsComponent } from '@ghostfolio/client/components/admin-settings/admin-settings.component';
import { AdminUsersComponent } from '@ghostfolio/client/components/admin-users/admin-users.component'; import { AdminUsersComponent } from '@ghostfolio/client/components/admin-users/admin-users.component';
import { AuthGuard } from '@ghostfolio/client/core/auth.guard'; import { AuthGuard } from '@ghostfolio/client/core/auth.guard';
import { paths } from '@ghostfolio/common/paths';
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router';
@ -20,22 +21,22 @@ const routes: Routes = [
title: $localize`Admin Control` title: $localize`Admin Control`
}, },
{ {
path: 'jobs', path: paths.jobs,
component: AdminJobsComponent, component: AdminJobsComponent,
title: $localize`Job Queue` title: $localize`Job Queue`
}, },
{ {
path: 'market-data', path: paths.marketData,
component: AdminMarketDataComponent, component: AdminMarketDataComponent,
title: $localize`Market Data` title: $localize`Market Data`
}, },
{ {
path: 'settings', path: paths.settings,
component: AdminSettingsComponent, component: AdminSettingsComponent,
title: $localize`Settings` title: $localize`Settings`
}, },
{ {
path: 'users', path: paths.users,
component: AdminUsersComponent, component: AdminUsersComponent,
title: $localize`Users` title: $localize`Users`
} }

11
apps/client/src/app/pages/admin/admin-page.component.ts

@ -1,4 +1,5 @@
import { TabConfiguration } from '@ghostfolio/common/interfaces'; import { TabConfiguration } from '@ghostfolio/common/interfaces';
import { paths } from '@ghostfolio/common/paths';
import { Component, OnDestroy, OnInit } from '@angular/core'; import { Component, OnDestroy, OnInit } from '@angular/core';
import { DeviceDetectorService } from 'ngx-device-detector'; import { DeviceDetectorService } from 'ngx-device-detector';
@ -26,27 +27,27 @@ export class AdminPageComponent implements OnDestroy, OnInit {
{ {
iconName: 'reader-outline', iconName: 'reader-outline',
label: $localize`Overview`, label: $localize`Overview`,
path: ['/admin'] path: ['/' + paths.admin]
}, },
{ {
iconName: 'settings-outline', iconName: 'settings-outline',
label: $localize`Settings`, label: $localize`Settings`,
path: ['/admin', 'settings'] path: ['/' + paths.admin, paths.settings]
}, },
{ {
iconName: 'server-outline', iconName: 'server-outline',
label: $localize`Market Data`, label: $localize`Market Data`,
path: ['/admin', 'market-data'] path: ['/' + paths.admin, paths.marketData]
}, },
{ {
iconName: 'flash-outline', iconName: 'flash-outline',
label: $localize`Job Queue`, label: $localize`Job Queue`,
path: ['/admin', 'jobs'] path: ['/' + paths.admin, paths.jobs]
}, },
{ {
iconName: 'people-outline', iconName: 'people-outline',
label: $localize`Users`, label: $localize`Users`,
path: ['/admin', 'users'] path: ['/' + paths.admin, paths.users]
} }
]; ];
} }

6
apps/client/src/app/pages/blog/2021/07/hallo-ghostfolio/hallo-ghostfolio-page.component.ts

@ -1,3 +1,5 @@
import { paths } from '@ghostfolio/common/paths';
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
@ -9,6 +11,6 @@ import { RouterModule } from '@angular/router';
templateUrl: './hallo-ghostfolio-page.html' templateUrl: './hallo-ghostfolio-page.html'
}) })
export class HalloGhostfolioPageComponent { export class HalloGhostfolioPageComponent {
public routerLinkPricing = ['/' + $localize`:snake-case:pricing`]; public routerLinkPricing = ['/' + paths.pricing];
public routerLinkResources = ['/' + $localize`:snake-case:resources`]; public routerLinkResources = ['/' + paths.resources];
} }

6
apps/client/src/app/pages/blog/2021/07/hello-ghostfolio/hello-ghostfolio-page.component.ts

@ -1,3 +1,5 @@
import { paths } from '@ghostfolio/common/paths';
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
@ -9,6 +11,6 @@ import { RouterModule } from '@angular/router';
templateUrl: './hello-ghostfolio-page.html' templateUrl: './hello-ghostfolio-page.html'
}) })
export class HelloGhostfolioPageComponent { export class HelloGhostfolioPageComponent {
public routerLinkPricing = ['/' + $localize`:snake-case:pricing`]; public routerLinkPricing = ['/' + paths.pricing];
public routerLinkResources = ['/' + $localize`:snake-case:resources`]; public routerLinkResources = ['/' + paths.resources];
} }

4
apps/client/src/app/pages/blog/2022/01/first-months-in-open-source/first-months-in-open-source-page.component.ts

@ -1,3 +1,5 @@
import { paths } from '@ghostfolio/common/paths';
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
@ -9,5 +11,5 @@ import { RouterModule } from '@angular/router';
templateUrl: './first-months-in-open-source-page.html' templateUrl: './first-months-in-open-source-page.html'
}) })
export class FirstMonthsInOpenSourcePageComponent { export class FirstMonthsInOpenSourcePageComponent {
public routerLinkPricing = ['/' + $localize`:snake-case:pricing`]; public routerLinkPricing = ['/' + paths.pricing];
} }

4
apps/client/src/app/pages/blog/2022/07/how-do-i-get-my-finances-in-order/how-do-i-get-my-finances-in-order-page.component.ts

@ -1,3 +1,5 @@
import { paths } from '@ghostfolio/common/paths';
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
@ -9,5 +11,5 @@ import { RouterModule } from '@angular/router';
templateUrl: './how-do-i-get-my-finances-in-order-page.html' templateUrl: './how-do-i-get-my-finances-in-order-page.html'
}) })
export class HowDoIGetMyFinancesInOrderPageComponent { export class HowDoIGetMyFinancesInOrderPageComponent {
public routerLinkResources = ['/' + $localize`:snake-case:resources`]; public routerLinkResources = ['/' + paths.resources];
} }

6
apps/client/src/app/pages/blog/2022/08/500-stars-on-github/500-stars-on-github-page.component.ts

@ -1,3 +1,5 @@
import { paths } from '@ghostfolio/common/paths';
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
@ -9,6 +11,6 @@ import { RouterModule } from '@angular/router';
templateUrl: './500-stars-on-github-page.html' templateUrl: './500-stars-on-github-page.html'
}) })
export class FiveHundredStarsOnGitHubPageComponent { export class FiveHundredStarsOnGitHubPageComponent {
public routerLinkMarkets = ['/' + $localize`:snake-case:markets`]; public routerLinkMarkets = ['/' + paths.markets];
public routerLinkPricing = ['/' + $localize`:snake-case:pricing`]; public routerLinkPricing = ['/' + paths.pricing];
} }

5
apps/client/src/app/pages/blog/2022/11/black-friday-2022/black-friday-2022-page.component.ts

@ -1,3 +1,4 @@
import { paths } from '@ghostfolio/common/paths';
import { GfPremiumIndicatorComponent } from '@ghostfolio/ui/premium-indicator'; import { GfPremiumIndicatorComponent } from '@ghostfolio/ui/premium-indicator';
import { Component } from '@angular/core'; import { Component } from '@angular/core';
@ -11,6 +12,6 @@ import { RouterModule } from '@angular/router';
templateUrl: './black-friday-2022-page.html' templateUrl: './black-friday-2022-page.html'
}) })
export class BlackFriday2022PageComponent { export class BlackFriday2022PageComponent {
public routerLinkFeatures = ['/' + $localize`:snake-case:features`]; public routerLinkFeatures = ['/' + paths.features];
public routerLinkPricing = ['/' + $localize`:snake-case:pricing`]; public routerLinkPricing = ['/' + paths.pricing];
} }

6
apps/client/src/app/pages/blog/2023/03/1000-stars-on-github/1000-stars-on-github-page.component.ts

@ -1,3 +1,5 @@
import { paths } from '@ghostfolio/common/paths';
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
@ -9,6 +11,6 @@ import { RouterModule } from '@angular/router';
templateUrl: './1000-stars-on-github-page.html' templateUrl: './1000-stars-on-github-page.html'
}) })
export class ThousandStarsOnGitHubPageComponent { export class ThousandStarsOnGitHubPageComponent {
public routerLinkFeatures = ['/' + $localize`:snake-case:features`]; public routerLinkFeatures = ['/' + paths.features];
public routerLinkPricing = ['/' + $localize`:snake-case:pricing`]; public routerLinkPricing = ['/' + paths.pricing];
} }

6
apps/client/src/app/pages/blog/2023/05/unlock-your-financial-potential-with-ghostfolio/unlock-your-financial-potential-with-ghostfolio-page.component.ts

@ -1,3 +1,5 @@
import { paths } from '@ghostfolio/common/paths';
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
@ -9,6 +11,6 @@ import { RouterModule } from '@angular/router';
templateUrl: './unlock-your-financial-potential-with-ghostfolio-page.html' templateUrl: './unlock-your-financial-potential-with-ghostfolio-page.html'
}) })
export class UnlockYourFinancialPotentialWithGhostfolioPageComponent { export class UnlockYourFinancialPotentialWithGhostfolioPageComponent {
public routerLinkFeatures = ['/' + $localize`:snake-case:features`]; public routerLinkFeatures = ['/' + paths.features];
public routerLinkResources = ['/' + $localize`:snake-case:resources`]; public routerLinkResources = ['/' + paths.resources];
} }

4
apps/client/src/app/pages/blog/2023/07/exploring-the-path-to-fire/exploring-the-path-to-fire-page.component.ts

@ -1,3 +1,5 @@
import { paths } from '@ghostfolio/common/paths';
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
@ -9,5 +11,5 @@ import { RouterModule } from '@angular/router';
templateUrl: './exploring-the-path-to-fire-page.html' templateUrl: './exploring-the-path-to-fire-page.html'
}) })
export class ExploringThePathToFirePageComponent { export class ExploringThePathToFirePageComponent {
public routerLinkFeatures = ['/' + $localize`:snake-case:features`]; public routerLinkFeatures = ['/' + paths.features];
} }

7
apps/client/src/app/pages/blog/2023/08/ghostfolio-joins-oss-friends/ghostfolio-joins-oss-friends-page.component.ts

@ -1,3 +1,5 @@
import { paths } from '@ghostfolio/common/paths';
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
@ -9,8 +11,5 @@ import { RouterModule } from '@angular/router';
templateUrl: './ghostfolio-joins-oss-friends-page.html' templateUrl: './ghostfolio-joins-oss-friends-page.html'
}) })
export class GhostfolioJoinsOssFriendsPageComponent { export class GhostfolioJoinsOssFriendsPageComponent {
public routerLinkAboutOssFriends = [ public routerLinkAboutOssFriends = ['/' + paths.about, paths.ossFriends];
'/' + $localize`:snake-case:about`,
'oss-friends'
];
} }

13
apps/client/src/app/pages/blog/2023/09/ghostfolio-2/ghostfolio-2-page.component.ts

@ -1,3 +1,5 @@
import { paths } from '@ghostfolio/common/paths';
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
@ -9,11 +11,8 @@ import { RouterModule } from '@angular/router';
templateUrl: './ghostfolio-2-page.html' templateUrl: './ghostfolio-2-page.html'
}) })
export class Ghostfolio2PageComponent { export class Ghostfolio2PageComponent {
public routerLinkAbout = ['/' + $localize`:snake-case:about`]; public routerLinkAbout = ['/' + paths.about];
public routerLinkAboutChangelog = [ public routerLinkAboutChangelog = ['/' + paths.about, paths.changelog];
'/' + $localize`:snake-case:about`, public routerLinkFeatures = ['/' + paths.features];
'changelog' public routerLinkMarkets = ['/' + paths.markets];
];
public routerLinkFeatures = ['/' + $localize`:snake-case:features`];
public routerLinkMarkets = ['/' + $localize`:snake-case:markets`];
} }

4
apps/client/src/app/pages/blog/2023/09/hacktoberfest-2023/hacktoberfest-2023-page.component.ts

@ -1,3 +1,5 @@
import { paths } from '@ghostfolio/common/paths';
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
@ -9,5 +11,5 @@ import { RouterModule } from '@angular/router';
templateUrl: './hacktoberfest-2023-page.html' templateUrl: './hacktoberfest-2023-page.html'
}) })
export class Hacktoberfest2023PageComponent { export class Hacktoberfest2023PageComponent {
public routerLinkAbout = ['/' + $localize`:snake-case:about`]; public routerLinkAbout = ['/' + paths.about];
} }

5
apps/client/src/app/pages/blog/2023/11/black-week-2023/black-week-2023-page.component.ts

@ -1,3 +1,4 @@
import { paths } from '@ghostfolio/common/paths';
import { GfPremiumIndicatorComponent } from '@ghostfolio/ui/premium-indicator'; import { GfPremiumIndicatorComponent } from '@ghostfolio/ui/premium-indicator';
import { Component } from '@angular/core'; import { Component } from '@angular/core';
@ -11,6 +12,6 @@ import { RouterModule } from '@angular/router';
templateUrl: './black-week-2023-page.html' templateUrl: './black-week-2023-page.html'
}) })
export class BlackWeek2023PageComponent { export class BlackWeek2023PageComponent {
public routerLinkFeatures = ['/' + $localize`:snake-case:features`]; public routerLinkFeatures = ['/' + paths.features];
public routerLinkPricing = ['/' + $localize`:snake-case:pricing`]; public routerLinkPricing = ['/' + paths.pricing];
} }

6
apps/client/src/app/pages/blog/2023/11/hacktoberfest-2023-debriefing/hacktoberfest-2023-debriefing-page.component.ts

@ -1,3 +1,5 @@
import { paths } from '@ghostfolio/common/paths';
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
@ -9,6 +11,6 @@ import { RouterModule } from '@angular/router';
templateUrl: './hacktoberfest-2023-debriefing-page.html' templateUrl: './hacktoberfest-2023-debriefing-page.html'
}) })
export class Hacktoberfest2023DebriefingPageComponent { export class Hacktoberfest2023DebriefingPageComponent {
public routerLinkAbout = ['/' + $localize`:snake-case:about`]; public routerLinkAbout = ['/' + paths.about];
public routerLinkFeatures = ['/' + $localize`:snake-case:features`]; public routerLinkFeatures = ['/' + paths.features];
} }

4
apps/client/src/app/pages/blog/2024/09/hacktoberfest-2024/hacktoberfest-2024-page.component.ts

@ -1,3 +1,5 @@
import { paths } from '@ghostfolio/common/paths';
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
@ -9,5 +11,5 @@ import { RouterModule } from '@angular/router';
templateUrl: './hacktoberfest-2024-page.html' templateUrl: './hacktoberfest-2024-page.html'
}) })
export class Hacktoberfest2024PageComponent { export class Hacktoberfest2024PageComponent {
public routerLinkAbout = ['/' + $localize`:snake-case:about`]; public routerLinkAbout = ['/' + paths.about];
} }

5
apps/client/src/app/pages/blog/2024/11/black-weeks-2024/black-weeks-2024-page.component.ts

@ -1,3 +1,4 @@
import { paths } from '@ghostfolio/common/paths';
import { GfPremiumIndicatorComponent } from '@ghostfolio/ui/premium-indicator'; import { GfPremiumIndicatorComponent } from '@ghostfolio/ui/premium-indicator';
import { Component } from '@angular/core'; import { Component } from '@angular/core';
@ -11,6 +12,6 @@ import { RouterModule } from '@angular/router';
templateUrl: './black-weeks-2024-page.html' templateUrl: './black-weeks-2024-page.html'
}) })
export class BlackWeeks2024PageComponent { export class BlackWeeks2024PageComponent {
public routerLinkFeatures = ['/' + $localize`:snake-case:features`]; public routerLinkFeatures = ['/' + paths.features];
public routerLinkPricing = ['/' + $localize`:snake-case:pricing`]; public routerLinkPricing = ['/' + paths.pricing];
} }

5
apps/client/src/app/pages/faq/faq-page-routing.module.ts

@ -1,4 +1,5 @@
import { AuthGuard } from '@ghostfolio/client/core/auth.guard'; import { AuthGuard } from '@ghostfolio/client/core/auth.guard';
import { paths } from '@ghostfolio/common/paths';
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router';
@ -17,12 +18,12 @@ const routes: Routes = [
) )
}, },
{ {
path: 'saas', path: paths.saas,
loadChildren: () => loadChildren: () =>
import('./saas/saas-page.module').then((m) => m.SaasPageModule) import('./saas/saas-page.module').then((m) => m.SaasPageModule)
}, },
{ {
path: 'self-hosting', path: paths.selfHosting,
loadChildren: () => loadChildren: () =>
import('./self-hosting/self-hosting-page.module').then( import('./self-hosting/self-hosting-page.module').then(
(m) => m.SelfHostingPageModule (m) => m.SelfHostingPageModule

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

@ -1,5 +1,6 @@
import { DataService } from '@ghostfolio/client/services/data.service'; import { DataService } from '@ghostfolio/client/services/data.service';
import { TabConfiguration } from '@ghostfolio/common/interfaces'; import { TabConfiguration } from '@ghostfolio/common/interfaces';
import { paths } from '@ghostfolio/common/paths';
import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import { Component, OnDestroy, OnInit } from '@angular/core'; import { Component, OnDestroy, OnInit } from '@angular/core';
@ -35,18 +36,18 @@ export class FaqPageComponent implements OnDestroy, OnInit {
{ {
iconName: 'reader-outline', iconName: 'reader-outline',
label: $localize`General`, label: $localize`General`,
path: ['/' + $localize`faq`] path: ['/' + paths.faq]
}, },
{ {
iconName: 'cloudy-outline', iconName: 'cloudy-outline',
label: $localize`Cloud` + ' (SaaS)', label: $localize`Cloud` + ' (SaaS)',
path: ['/' + $localize`faq`, 'saas'], path: ['/' + paths.faq, paths.saas],
showCondition: this.hasPermissionForSubscription showCondition: this.hasPermissionForSubscription
}, },
{ {
iconName: 'server-outline', iconName: 'server-outline',
label: $localize`Self-Hosting`, label: $localize`Self-Hosting`,
path: ['/' + $localize`faq`, $localize`self-hosting`] path: ['/' + paths.faq, paths.selfHosting]
} }
]; ];
} }

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

@ -1,5 +1,6 @@
import { UserService } from '@ghostfolio/client/services/user/user.service'; import { UserService } from '@ghostfolio/client/services/user/user.service';
import { User } from '@ghostfolio/common/interfaces'; import { User } from '@ghostfolio/common/interfaces';
import { paths } from '@ghostfolio/common/paths';
import { ChangeDetectorRef, Component, OnDestroy } from '@angular/core'; import { ChangeDetectorRef, Component, OnDestroy } from '@angular/core';
import { Subject, takeUntil } from 'rxjs'; import { Subject, takeUntil } from 'rxjs';
@ -12,10 +13,8 @@ import { Subject, takeUntil } from 'rxjs';
standalone: false standalone: false
}) })
export class FaqOverviewPageComponent implements OnDestroy { export class FaqOverviewPageComponent implements OnDestroy {
public pricingUrl = public pricingUrl = `https://ghostfol.io/${document.documentElement.lang}/${paths.pricing}`;
`https://ghostfol.io/${document.documentElement.lang}/` + public routerLinkFeatures = ['/' + paths.features];
$localize`:snake-case:pricing`;
public routerLinkFeatures = ['/' + $localize`:snake-case:features`];
public user: User; public user: User;
private unsubscribeSubject = new Subject<void>(); private unsubscribeSubject = new Subject<void>();

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

@ -1,5 +1,6 @@
import { UserService } from '@ghostfolio/client/services/user/user.service'; import { UserService } from '@ghostfolio/client/services/user/user.service';
import { User } from '@ghostfolio/common/interfaces'; import { User } from '@ghostfolio/common/interfaces';
import { paths } from '@ghostfolio/common/paths';
import { ChangeDetectorRef, Component, OnDestroy } from '@angular/core'; import { ChangeDetectorRef, Component, OnDestroy } from '@angular/core';
import { Subject, takeUntil } from 'rxjs'; import { Subject, takeUntil } from 'rxjs';
@ -12,11 +13,9 @@ import { Subject, takeUntil } from 'rxjs';
standalone: false standalone: false
}) })
export class SaasPageComponent implements OnDestroy { export class SaasPageComponent implements OnDestroy {
public pricingUrl = public pricingUrl = `https://ghostfol.io/${document.documentElement.lang}/${paths.pricing}`;
`https://ghostfol.io/${document.documentElement.lang}/` + public routerLinkMarkets = ['/' + paths.markets];
$localize`:snake-case:pricing`; public routerLinkRegister = ['/' + paths.register];
public routerLinkMarkets = ['/' + $localize`:snake-case:markets`];
public routerLinkRegister = ['/' + $localize`:snake-case:register`];
public user: User; public user: User;
private unsubscribeSubject = new Subject<void>(); private unsubscribeSubject = new Subject<void>();

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

@ -1,3 +1,5 @@
import { paths } from '@ghostfolio/common/paths';
import { Component, OnDestroy } from '@angular/core'; import { Component, OnDestroy } from '@angular/core';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
@ -9,9 +11,7 @@ import { Subject } from 'rxjs';
standalone: false standalone: false
}) })
export class SelfHostingPageComponent implements OnDestroy { export class SelfHostingPageComponent implements OnDestroy {
public pricingUrl = public pricingUrl = `https://ghostfol.io/${document.documentElement.lang}/${paths.pricing}`;
`https://ghostfol.io/${document.documentElement.lang}/` +
$localize`:snake-case:pricing`;
private unsubscribeSubject = new Subject<void>(); private unsubscribeSubject = new Subject<void>();

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

@ -1,6 +1,7 @@
import { DataService } from '@ghostfolio/client/services/data.service'; import { DataService } from '@ghostfolio/client/services/data.service';
import { UserService } from '@ghostfolio/client/services/user/user.service'; import { UserService } from '@ghostfolio/client/services/user/user.service';
import { InfoItem, User } from '@ghostfolio/common/interfaces'; import { InfoItem, User } from '@ghostfolio/common/interfaces';
import { paths } from '@ghostfolio/common/paths';
import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import { GfPremiumIndicatorComponent } from '@ghostfolio/ui/premium-indicator'; import { GfPremiumIndicatorComponent } from '@ghostfolio/ui/premium-indicator';
@ -25,8 +26,8 @@ import { Subject, takeUntil } from 'rxjs';
export class GfFeaturesPageComponent implements OnDestroy { export class GfFeaturesPageComponent implements OnDestroy {
public hasPermissionForSubscription: boolean; public hasPermissionForSubscription: boolean;
public info: InfoItem; public info: InfoItem;
public routerLinkRegister = ['/' + $localize`:snake-case:register`]; public routerLinkRegister = ['/' + paths.register];
public routerLinkResources = ['/' + $localize`:snake-case:resources`]; public routerLinkResources = ['/' + paths.resources];
public user: User; public user: User;
private unsubscribeSubject = new Subject<void>(); private unsubscribeSubject = new Subject<void>();

14
apps/client/src/app/pages/home/home-page-routing.module.ts

@ -4,6 +4,7 @@ import { HomeOverviewComponent } from '@ghostfolio/client/components/home-overvi
import { HomeSummaryComponent } from '@ghostfolio/client/components/home-summary/home-summary.component'; import { HomeSummaryComponent } from '@ghostfolio/client/components/home-summary/home-summary.component';
import { HomeWatchlistComponent } from '@ghostfolio/client/components/home-watchlist/home-watchlist.component'; import { HomeWatchlistComponent } from '@ghostfolio/client/components/home-watchlist/home-watchlist.component';
import { AuthGuard } from '@ghostfolio/client/core/auth.guard'; import { AuthGuard } from '@ghostfolio/client/core/auth.guard';
import { paths } from '@ghostfolio/common/paths';
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router';
@ -19,27 +20,22 @@ const routes: Routes = [
component: HomeOverviewComponent component: HomeOverviewComponent
}, },
{ {
path: 'holdings', path: paths.holdings,
component: HomeHoldingsComponent, component: HomeHoldingsComponent,
title: $localize`Holdings` title: $localize`Holdings`
}, },
{ {
path: 'holdings', path: paths.summary,
component: HomeHoldingsComponent,
title: $localize`Holdings`
},
{
path: 'summary',
component: HomeSummaryComponent, component: HomeSummaryComponent,
title: $localize`Summary` title: $localize`Summary`
}, },
{ {
path: 'market', path: paths.market,
component: HomeMarketComponent, component: HomeMarketComponent,
title: $localize`Markets` title: $localize`Markets`
}, },
{ {
path: 'watchlist', path: paths.watchlist,
component: HomeWatchlistComponent, component: HomeWatchlistComponent,
title: $localize`Watchlist` title: $localize`Watchlist`
} }

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

@ -1,6 +1,7 @@
import { ImpersonationStorageService } from '@ghostfolio/client/services/impersonation-storage.service'; import { ImpersonationStorageService } from '@ghostfolio/client/services/impersonation-storage.service';
import { UserService } from '@ghostfolio/client/services/user/user.service'; import { UserService } from '@ghostfolio/client/services/user/user.service';
import { TabConfiguration, User } from '@ghostfolio/common/interfaces'; import { TabConfiguration, User } from '@ghostfolio/common/interfaces';
import { paths } from '@ghostfolio/common/paths';
import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
import { DeviceDetectorService } from 'ngx-device-detector'; import { DeviceDetectorService } from 'ngx-device-detector';
@ -36,27 +37,27 @@ export class HomePageComponent implements OnDestroy, OnInit {
{ {
iconName: 'analytics-outline', iconName: 'analytics-outline',
label: $localize`Overview`, label: $localize`Overview`,
path: ['/home'] path: ['/' + paths.home]
}, },
{ {
iconName: 'wallet-outline', iconName: 'wallet-outline',
label: $localize`Holdings`, label: $localize`Holdings`,
path: ['/home', 'holdings'] path: ['/' + paths.home, paths.holdings]
}, },
{ {
iconName: 'reader-outline', iconName: 'reader-outline',
label: $localize`Summary`, label: $localize`Summary`,
path: ['/home', 'summary'] path: ['/' + paths.home, paths.summary]
}, },
{ {
iconName: 'bookmark-outline', iconName: 'bookmark-outline',
label: $localize`Watchlist`, label: $localize`Watchlist`,
path: ['/home', 'watchlist'] path: ['/' + paths.home, paths.watchlist]
}, },
{ {
iconName: 'newspaper-outline', iconName: 'newspaper-outline',
label: $localize`Markets`, label: $localize`Markets`,
path: ['/home', 'market'] path: ['/' + paths.home, paths.market]
} }
]; ];

5
apps/client/src/app/pages/landing/landing-page.component.ts

@ -1,5 +1,6 @@
import { DataService } from '@ghostfolio/client/services/data.service'; import { DataService } from '@ghostfolio/client/services/data.service';
import { Statistics } from '@ghostfolio/common/interfaces'; import { Statistics } from '@ghostfolio/common/interfaces';
import { paths } from '@ghostfolio/common/paths';
import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import { Component, OnDestroy, OnInit } from '@angular/core'; import { Component, OnDestroy, OnInit } from '@angular/core';
@ -24,8 +25,8 @@ export class LandingPageComponent implements OnDestroy, OnInit {
public hasPermissionForStatistics: boolean; public hasPermissionForStatistics: boolean;
public hasPermissionForSubscription: boolean; public hasPermissionForSubscription: boolean;
public hasPermissionToCreateUser: boolean; public hasPermissionToCreateUser: boolean;
public routerLinkAbout = ['/' + $localize`:snake-case:about`]; public routerLinkAbout = ['/' + paths.about];
public routerLinkRegister = ['/' + $localize`:snake-case:register`]; public routerLinkRegister = ['/' + paths.register];
public statistics: Statistics; public statistics: Statistics;
public testimonials = [ public testimonials = [
{ {

9
apps/client/src/app/pages/portfolio/portfolio-page-routing.module.ts

@ -1,4 +1,5 @@
import { AuthGuard } from '@ghostfolio/client/core/auth.guard'; import { AuthGuard } from '@ghostfolio/client/core/auth.guard';
import { paths } from '@ghostfolio/common/paths';
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router';
@ -17,26 +18,26 @@ const routes: Routes = [
) )
}, },
{ {
path: 'activities', path: paths.activities,
loadChildren: () => loadChildren: () =>
import('./activities/activities-page.module').then( import('./activities/activities-page.module').then(
(m) => m.ActivitiesPageModule (m) => m.ActivitiesPageModule
) )
}, },
{ {
path: 'allocations', path: paths.allocations,
loadChildren: () => loadChildren: () =>
import('./allocations/allocations-page.module').then( import('./allocations/allocations-page.module').then(
(m) => m.AllocationsPageModule (m) => m.AllocationsPageModule
) )
}, },
{ {
path: 'fire', path: paths.fire,
loadChildren: () => loadChildren: () =>
import('./fire/fire-page.module').then((m) => m.FirePageModule) import('./fire/fire-page.module').then((m) => m.FirePageModule)
}, },
{ {
path: 'x-ray', path: paths.xRay,
loadChildren: () => loadChildren: () =>
import('./x-ray/x-ray-page.module').then((m) => m.XRayPageModule) import('./x-ray/x-ray-page.module').then((m) => m.XRayPageModule)
} }

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

@ -1,5 +1,6 @@
import { UserService } from '@ghostfolio/client/services/user/user.service'; import { UserService } from '@ghostfolio/client/services/user/user.service';
import { TabConfiguration, User } from '@ghostfolio/common/interfaces'; import { TabConfiguration, User } from '@ghostfolio/common/interfaces';
import { paths } from '@ghostfolio/common/paths';
import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
import { DeviceDetectorService } from 'ngx-device-detector'; import { DeviceDetectorService } from 'ngx-device-detector';
@ -33,27 +34,27 @@ export class PortfolioPageComponent implements OnDestroy, OnInit {
{ {
iconName: 'analytics-outline', iconName: 'analytics-outline',
label: $localize`Analysis`, label: $localize`Analysis`,
path: ['/portfolio'] path: ['/' + paths.portfolio]
}, },
{ {
iconName: 'swap-vertical-outline', iconName: 'swap-vertical-outline',
label: $localize`Activities`, label: $localize`Activities`,
path: ['/portfolio', 'activities'] path: ['/' + paths.portfolio, paths.activities]
}, },
{ {
iconName: 'pie-chart-outline', iconName: 'pie-chart-outline',
label: $localize`Allocations`, label: $localize`Allocations`,
path: ['/portfolio', 'allocations'] path: ['/' + paths.portfolio, paths.allocations]
}, },
{ {
iconName: 'calculator-outline', iconName: 'calculator-outline',
label: 'FIRE ', label: 'FIRE ',
path: ['/portfolio', 'fire'] path: ['/' + paths.portfolio, paths.fire]
}, },
{ {
iconName: 'scan-outline', iconName: 'scan-outline',
label: 'X-ray', label: 'X-ray',
path: ['/portfolio', 'x-ray'] path: ['/' + paths.portfolio, paths.xRay]
} }
]; ];
this.user = state.user; this.user = state.user;

5
apps/client/src/app/pages/pricing/pricing-page.component.ts

@ -2,6 +2,7 @@ import { NotificationService } from '@ghostfolio/client/core/notification/notifi
import { DataService } from '@ghostfolio/client/services/data.service'; import { DataService } from '@ghostfolio/client/services/data.service';
import { UserService } from '@ghostfolio/client/services/user/user.service'; import { UserService } from '@ghostfolio/client/services/user/user.service';
import { User } from '@ghostfolio/common/interfaces'; import { User } from '@ghostfolio/common/interfaces';
import { paths } from '@ghostfolio/common/paths';
import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import { translate } from '@ghostfolio/ui/i18n'; import { translate } from '@ghostfolio/ui/i18n';
@ -40,8 +41,8 @@ export class PricingPageComponent implements OnDestroy, OnInit {
public professionalDataProviderTooltipPremium = translate( public professionalDataProviderTooltipPremium = translate(
'PROFESSIONAL_DATA_PROVIDER_TOOLTIP_PREMIUM' 'PROFESSIONAL_DATA_PROVIDER_TOOLTIP_PREMIUM'
); );
public routerLinkFeatures = ['/' + $localize`:snake-case:features`]; public routerLinkFeatures = ['/' + paths.features];
public routerLinkRegister = ['/' + $localize`:snake-case:register`]; public routerLinkRegister = ['/' + paths.register];
public user: User; public user: User;
private unsubscribeSubject = new Subject<void>(); private unsubscribeSubject = new Subject<void>();

5
apps/client/src/app/pages/register/show-access-token-dialog/show-access-token-dialog.component.ts

@ -1,4 +1,5 @@
import { DataService } from '@ghostfolio/client/services/data.service'; import { DataService } from '@ghostfolio/client/services/data.service';
import { paths } from '@ghostfolio/common/paths';
import { import {
ChangeDetectionStrategy, ChangeDetectionStrategy,
@ -30,8 +31,8 @@ export class ShowAccessTokenDialog {
public isDisclaimerChecked = false; public isDisclaimerChecked = false;
public role: string; public role: string;
public routerLinkAboutTermsOfService = [ public routerLinkAboutTermsOfService = [
'/' + $localize`:snake-case:about`, '/' + paths.about,
$localize`:snake-case:terms-of-service` paths.termsOfService
]; ];
private unsubscribeSubject = new Subject<void>(); private unsubscribeSubject = new Subject<void>();

5
apps/client/src/app/pages/resources/glossary/resources-glossary.component.ts

@ -1,5 +1,6 @@
import { DataService } from '@ghostfolio/client/services/data.service'; import { DataService } from '@ghostfolio/client/services/data.service';
import { InfoItem } from '@ghostfolio/common/interfaces'; import { InfoItem } from '@ghostfolio/common/interfaces';
import { paths } from '@ghostfolio/common/paths';
import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
@ -14,8 +15,8 @@ export class ResourcesGlossaryPageComponent implements OnInit {
public hasPermissionForSubscription: boolean; public hasPermissionForSubscription: boolean;
public info: InfoItem; public info: InfoItem;
public routerLinkResourcesPersonalFinanceTools = [ public routerLinkResourcesPersonalFinanceTools = [
'/' + $localize`:snake-case:resources`, '/' + paths.resources,
'personal-finance-tools' paths.personalFinanceTools
]; ];
public constructor(private dataService: DataService) { public constructor(private dataService: DataService) {

19
apps/client/src/app/pages/resources/overview/resources-overview.component.ts

@ -1,3 +1,5 @@
import { paths } from '@ghostfolio/common/paths';
import { Component } from '@angular/core'; import { Component } from '@angular/core';
@Component({ @Component({
@ -12,34 +14,25 @@ export class ResourcesOverviewComponent {
title: 'Frequently Asked Questions (FAQ)', title: 'Frequently Asked Questions (FAQ)',
description: description:
'Find quick answers to commonly asked questions about Ghostfolio in our Frequently Asked Questions (FAQ) section.', 'Find quick answers to commonly asked questions about Ghostfolio in our Frequently Asked Questions (FAQ) section.',
link: ['/' + $localize`:snake-case:faq`] link: ['/' + paths.faq]
}, },
{ {
title: 'Guides', title: 'Guides',
description: description:
'Explore our guides to help you get started with investing and managing your finances.', 'Explore our guides to help you get started with investing and managing your finances.',
link: [ link: ['/' + paths.resources, paths.guides]
'/' + $localize`:snake-case:resources`,
$localize`:snake-case:guides`
]
}, },
{ {
title: 'Markets', title: 'Markets',
description: description:
'Access various market resources and tools to stay informed about financial markets.', 'Access various market resources and tools to stay informed about financial markets.',
link: [ link: ['/' + paths.resources, paths.markets]
'/' + $localize`:snake-case:resources`,
$localize`:snake-case:markets`
]
}, },
{ {
title: 'Glossary', title: 'Glossary',
description: description:
'Learn key financial terms and concepts in our comprehensive glossary.', 'Learn key financial terms and concepts in our comprehensive glossary.',
link: [ link: ['/' + paths.resources, paths.glossary]
'/' + $localize`:snake-case:resources`,
$localize`:snake-case:glossary`
]
} }
]; ];
} }

3
apps/client/src/app/pages/resources/personal-finance-tools/personal-finance-tools-page-routing.module.ts

@ -1,4 +1,5 @@
import { AuthGuard } from '@ghostfolio/client/core/auth.guard'; import { AuthGuard } from '@ghostfolio/client/core/auth.guard';
import { paths } from '@ghostfolio/common/paths';
import { personalFinanceTools } from '@ghostfolio/common/personal-finance-tools'; import { personalFinanceTools } from '@ghostfolio/common/personal-finance-tools';
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
@ -23,7 +24,7 @@ const routes: Routes = [
return GfProductPageComponent; return GfProductPageComponent;
} }
), ),
path: $localize`open-source-alternative-to` + `-${alias ?? key}`, path: `${paths.openSourceAlternativeTo}-${alias ?? key}`,
title: $localize`Open Source Alternative to ${name}` title: $localize`Open Source Alternative to ${name}`
}; };
}) })

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

@ -1,3 +1,4 @@
import { paths } from '@ghostfolio/common/paths';
import { personalFinanceTools } from '@ghostfolio/common/personal-finance-tools'; import { personalFinanceTools } from '@ghostfolio/common/personal-finance-tools';
import { Component, OnDestroy } from '@angular/core'; import { Component, OnDestroy } from '@angular/core';
@ -11,12 +12,12 @@ import { Subject } from 'rxjs';
standalone: false standalone: false
}) })
export class PersonalFinanceToolsPageComponent implements OnDestroy { export class PersonalFinanceToolsPageComponent implements OnDestroy {
public pathAlternativeTo = $localize`open-source-alternative-to` + '-'; public pathAlternativeTo = paths.openSourceAlternativeTo + '-';
public pathResources = '/' + $localize`resources`; public pathResources = '/' + paths.resources;
public personalFinanceTools = personalFinanceTools.sort((a, b) => { public personalFinanceTools = personalFinanceTools.sort((a, b) => {
return a.name.localeCompare(b.name, undefined, { sensitivity: 'base' }); return a.name.localeCompare(b.name, undefined, { sensitivity: 'base' });
}); });
public routerLinkAbout = ['/' + $localize`:snake-case:about`]; public routerLinkAbout = ['/' + paths.about];
private unsubscribeSubject = new Subject<void>(); private unsubscribeSubject = new Subject<void>();

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

@ -1,5 +1,6 @@
import { DataService } from '@ghostfolio/client/services/data.service'; import { DataService } from '@ghostfolio/client/services/data.service';
import { Product } from '@ghostfolio/common/interfaces'; import { Product } from '@ghostfolio/common/interfaces';
import { paths } from '@ghostfolio/common/paths';
import { personalFinanceTools } from '@ghostfolio/common/personal-finance-tools'; import { personalFinanceTools } from '@ghostfolio/common/personal-finance-tools';
import { translate } from '@ghostfolio/ui/i18n'; import { translate } from '@ghostfolio/ui/i18n';
@ -19,11 +20,11 @@ export class GfProductPageComponent implements OnInit {
public price: number; public price: number;
public product1: Product; public product1: Product;
public product2: Product; public product2: Product;
public routerLinkAbout = ['/' + $localize`:snake-case:about`]; public routerLinkAbout = ['/' + paths.about];
public routerLinkFeatures = ['/' + $localize`:snake-case:features`]; public routerLinkFeatures = ['/' + paths.features];
public routerLinkResourcesPersonalFinanceTools = [ public routerLinkResourcesPersonalFinanceTools = [
'/' + $localize`:snake-case:resources`, '/' + paths.resources,
'personal-finance-tools' paths.personalFinanceTools
]; ];
public tags: string[]; public tags: string[];

9
apps/client/src/app/pages/resources/resources-page-routing.module.ts

@ -1,4 +1,5 @@
import { AuthGuard } from '@ghostfolio/client/core/auth.guard'; import { AuthGuard } from '@ghostfolio/client/core/auth.guard';
import { paths } from '@ghostfolio/common/paths';
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router';
@ -18,27 +19,27 @@ const routes: Routes = [
) )
}, },
{ {
path: $localize`:snake-case:glossary`, path: paths.glossary,
loadChildren: () => loadChildren: () =>
import('./glossary/resources-glossary.module').then( import('./glossary/resources-glossary.module').then(
(m) => m.ResourcesGlossaryPageModule (m) => m.ResourcesGlossaryPageModule
) )
}, },
{ {
path: $localize`:snake-case:guides`, path: paths.guides,
loadChildren: () => loadChildren: () =>
import('./guides/resources-guides.module').then( import('./guides/resources-guides.module').then(
(m) => m.ResourcesGuidesModule (m) => m.ResourcesGuidesModule
) )
}, },
{ {
path: $localize`:snake-case:markets`, path: paths.markets,
loadChildren: () => loadChildren: () =>
import('./markets/resources-markets.module').then( import('./markets/resources-markets.module').then(
(m) => m.ResourcesMarketsModule (m) => m.ResourcesMarketsModule
) )
}, },
...['personal-finance-tools'].map((path) => ({ ...[paths.personalFinanceTools].map((path) => ({
path, path,
loadChildren: () => loadChildren: () =>
import( import(

8
apps/client/src/app/pages/resources/resources-page.component.ts

@ -1,3 +1,5 @@
import { paths } from '@ghostfolio/common/paths';
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { DeviceDetectorService } from 'ngx-device-detector'; import { DeviceDetectorService } from 'ngx-device-detector';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
@ -18,17 +20,17 @@ export class ResourcesPageComponent implements OnInit {
iconName: 'reader-outline' iconName: 'reader-outline'
}, },
{ {
path: 'guides', path: paths.guides,
label: $localize`Guides`, label: $localize`Guides`,
iconName: 'book-outline' iconName: 'book-outline'
}, },
{ {
path: 'markets', path: paths.markets,
label: $localize`Markets`, label: $localize`Markets`,
iconName: 'newspaper-outline' iconName: 'newspaper-outline'
}, },
{ {
path: 'glossary', path: paths.glossary,
label: $localize`Glossary`, label: $localize`Glossary`,
iconName: 'library-outline' iconName: 'library-outline'
} }

5
apps/client/src/app/pages/user-account/user-account-page-routing.module.ts

@ -2,6 +2,7 @@ import { UserAccountAccessComponent } from '@ghostfolio/client/components/user-a
import { UserAccountMembershipComponent } from '@ghostfolio/client/components/user-account-membership/user-account-membership.component'; import { UserAccountMembershipComponent } from '@ghostfolio/client/components/user-account-membership/user-account-membership.component';
import { UserAccountSettingsComponent } from '@ghostfolio/client/components/user-account-settings/user-account-settings.component'; import { UserAccountSettingsComponent } from '@ghostfolio/client/components/user-account-settings/user-account-settings.component';
import { AuthGuard } from '@ghostfolio/client/core/auth.guard'; import { AuthGuard } from '@ghostfolio/client/core/auth.guard';
import { paths } from '@ghostfolio/common/paths';
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router';
@ -18,12 +19,12 @@ const routes: Routes = [
title: $localize`Settings` title: $localize`Settings`
}, },
{ {
path: 'membership', path: paths.membership,
component: UserAccountMembershipComponent, component: UserAccountMembershipComponent,
title: $localize`Membership` title: $localize`Membership`
}, },
{ {
path: 'access', path: paths.access,
component: UserAccountAccessComponent, component: UserAccountAccessComponent,
title: $localize`Access` title: $localize`Access`
} }

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

@ -1,5 +1,6 @@
import { UserService } from '@ghostfolio/client/services/user/user.service'; import { UserService } from '@ghostfolio/client/services/user/user.service';
import { TabConfiguration, User } from '@ghostfolio/common/interfaces'; import { TabConfiguration, User } from '@ghostfolio/common/interfaces';
import { paths } from '@ghostfolio/common/paths';
import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
import { DeviceDetectorService } from 'ngx-device-detector'; import { DeviceDetectorService } from 'ngx-device-detector';
@ -34,18 +35,18 @@ export class UserAccountPageComponent implements OnDestroy, OnInit {
{ {
iconName: 'settings-outline', iconName: 'settings-outline',
label: $localize`Settings`, label: $localize`Settings`,
path: ['/account'] path: ['/' + paths.account]
}, },
{ {
iconName: 'diamond-outline', iconName: 'diamond-outline',
label: $localize`Membership`, label: $localize`Membership`,
path: ['/account/membership'], path: ['/' + paths.account, paths.membership],
showCondition: !!this.user?.subscription showCondition: !!this.user?.subscription
}, },
{ {
iconName: 'key-outline', iconName: 'key-outline',
label: $localize`Access`, label: $localize`Access`,
path: ['/account', 'access'] path: ['/' + paths.account, paths.access]
} }
]; ];

3
apps/client/src/app/pages/zen/zen-page-routing.module.ts

@ -1,6 +1,7 @@
import { HomeHoldingsComponent } from '@ghostfolio/client/components/home-holdings/home-holdings.component'; import { HomeHoldingsComponent } from '@ghostfolio/client/components/home-holdings/home-holdings.component';
import { HomeOverviewComponent } from '@ghostfolio/client/components/home-overview/home-overview.component'; import { HomeOverviewComponent } from '@ghostfolio/client/components/home-overview/home-overview.component';
import { AuthGuard } from '@ghostfolio/client/core/auth.guard'; import { AuthGuard } from '@ghostfolio/client/core/auth.guard';
import { paths } from '@ghostfolio/common/paths';
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router';
@ -12,7 +13,7 @@ const routes: Routes = [
canActivate: [AuthGuard], canActivate: [AuthGuard],
children: [ children: [
{ path: '', component: HomeOverviewComponent }, { path: '', component: HomeOverviewComponent },
{ path: 'holdings', component: HomeHoldingsComponent } { path: paths.holdings, component: HomeHoldingsComponent }
], ],
component: ZenPageComponent, component: ZenPageComponent,
path: '', path: '',

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

@ -1,5 +1,6 @@
import { UserService } from '@ghostfolio/client/services/user/user.service'; import { UserService } from '@ghostfolio/client/services/user/user.service';
import { TabConfiguration, User } from '@ghostfolio/common/interfaces'; import { TabConfiguration, User } from '@ghostfolio/common/interfaces';
import { paths } from '@ghostfolio/common/paths';
import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
import { DeviceDetectorService } from 'ngx-device-detector'; import { DeviceDetectorService } from 'ngx-device-detector';
@ -33,12 +34,12 @@ export class ZenPageComponent implements OnDestroy, OnInit {
{ {
iconName: 'analytics-outline', iconName: 'analytics-outline',
label: $localize`Overview`, label: $localize`Overview`,
path: ['/zen'] path: ['/' + paths.zen]
}, },
{ {
iconName: 'wallet-outline', iconName: 'wallet-outline',
label: $localize`Holdings`, label: $localize`Holdings`,
path: ['/zen', 'holdings'] path: ['/' + paths.zen, paths.holdings]
} }
]; ];
this.user = state.user; this.user = state.user;

714
apps/client/src/locales/messages.ca.xlf

File diff suppressed because it is too large

892
apps/client/src/locales/messages.de.xlf

File diff suppressed because it is too large

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

File diff suppressed because it is too large

892
apps/client/src/locales/messages.fr.xlf

File diff suppressed because it is too large

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

File diff suppressed because it is too large

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

File diff suppressed because it is too large

712
apps/client/src/locales/messages.pl.xlf

File diff suppressed because it is too large

892
apps/client/src/locales/messages.pt.xlf

File diff suppressed because it is too large

712
apps/client/src/locales/messages.tr.xlf

File diff suppressed because it is too large

714
apps/client/src/locales/messages.uk.xlf

File diff suppressed because it is too large

706
apps/client/src/locales/messages.xlf

File diff suppressed because it is too large

716
apps/client/src/locales/messages.zh.xlf

File diff suppressed because it is too large

53
libs/common/src/lib/paths.ts

@ -0,0 +1,53 @@
import '@angular/localize/init';
export const paths = {
access: 'access',
account: 'account',
accounts: 'accounts',
activities: 'activities',
admin: 'admin',
allocations: 'allocations',
api: 'api',
auth: 'auth',
blog: 'blog',
demo: 'demo',
fire: 'fire',
holdings: 'holdings',
home: 'home',
i18n: 'i18n',
jobs: 'jobs',
market: 'market',
marketData: 'market-data',
membership: 'membership',
open: 'open',
personalFinanceTools: 'personal-finance-tools',
portfolio: 'portfolio',
public: 'p',
saas: 'saas',
settings: 'settings',
start: 'start',
summary: 'summary',
users: 'users',
watchlist: 'watchlist',
webauthn: 'webauthn',
xRay: 'x-ray',
zen: 'zen',
// Localized paths (public-facing pages)
about: $localize`:snake-case:about`,
changelog: $localize`:snake-case:changelog`,
faq: $localize`:snake-case:faq`,
features: $localize`:snake-case:features`,
glossary: $localize`:snake-case:glossary`,
guides: $localize`:snake-case:guides`,
license: $localize`:snake-case:license`,
markets: $localize`:snake-case:markets`,
openSourceAlternativeTo: $localize`:snake-case:open-source-alternative-to`,
ossFriends: $localize`:snake-case:oss-friends`,
pricing: $localize`:snake-case:pricing`,
privacyPolicy: $localize`:snake-case:privacy-policy`,
register: $localize`:snake-case:register`,
resources: $localize`:snake-case:resources`,
selfHosting: $localize`:snake-case:self-hosting`,
termsOfService: $localize`:snake-case:terms-of-service`
};

4
libs/ui/src/lib/membership-card/membership-card.component.ts

@ -1,3 +1,5 @@
import { paths } from '@ghostfolio/common/paths';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { import {
CUSTOM_ELEMENTS_SCHEMA, CUSTOM_ELEMENTS_SCHEMA,
@ -27,7 +29,7 @@ export class GfMembershipCardComponent {
@Output() generateApiKeyClicked = new EventEmitter<void>(); @Output() generateApiKeyClicked = new EventEmitter<void>();
public routerLinkPricing = ['/' + $localize`:snake-case:pricing`]; public routerLinkPricing = ['/' + paths.pricing];
public onGenerateApiKey(event: MouseEvent) { public onGenerateApiKey(event: MouseEvent) {
event.preventDefault(); event.preventDefault();

4
libs/ui/src/lib/premium-indicator/premium-indicator.component.ts

@ -1,3 +1,5 @@
import { paths } from '@ghostfolio/common/paths';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { import {
CUSTOM_ELEMENTS_SCHEMA, CUSTOM_ELEMENTS_SCHEMA,
@ -18,5 +20,5 @@ import { RouterModule } from '@angular/router';
export class GfPremiumIndicatorComponent { export class GfPremiumIndicatorComponent {
@Input() enableLink = true; @Input() enableLink = true;
public routerLinkPricing = ['/' + $localize`:snake-case:pricing`]; public routerLinkPricing = ['/' + paths.pricing];
} }

Loading…
Cancel
Save