Browse Source

Merge branch 'main' of https://github.com/aleskrin/ghostfolio into improve-spanish

pull/4924/head
Alesky Zakarin 2 weeks ago
parent
commit
bd7801a967
  1. 15
      CHANGELOG.md
  2. 8
      DEVELOPMENT.md
  3. 6
      apps/api/src/app/app.module.ts
  4. 2
      apps/api/src/app/endpoints/ai/ai.service.ts
  5. 12
      apps/api/src/main.ts
  6. 43
      apps/client/src/app/app-routing.module.ts
  7. 54
      apps/client/src/app/app.component.ts
  8. 4
      apps/client/src/app/components/access-table/access-table.component.ts
  9. 4
      apps/client/src/app/components/admin-settings/admin-settings.component.ts
  10. 8
      apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.ts
  11. 13
      apps/client/src/app/components/header/header.component.html
  12. 25
      apps/client/src/app/components/header/header.component.ts
  13. 4
      apps/client/src/app/components/subscription-interstitial-dialog/subscription-interstitial-dialog.component.ts
  14. 4
      apps/client/src/app/components/user-account-membership/user-account-membership.component.ts
  15. 28
      apps/client/src/app/core/auth.guard.ts
  16. 10
      apps/client/src/app/core/http-response.interceptor.ts
  17. 14
      apps/client/src/app/pages/about/about-page-routing.module.ts
  18. 26
      apps/client/src/app/pages/about/about-page.component.ts
  19. 2
      apps/client/src/app/pages/about/about-page.html
  20. 3
      apps/client/src/app/pages/about/changelog/changelog-page-routing.module.ts
  21. 3
      apps/client/src/app/pages/about/license/license-page-routing.module.ts
  22. 3
      apps/client/src/app/pages/about/oss-friends/oss-friends-page-routing.module.ts
  23. 6
      apps/client/src/app/pages/about/overview/about-overview-page.component.ts
  24. 3
      apps/client/src/app/pages/about/privacy-policy/privacy-policy-page-routing.module.ts
  25. 3
      apps/client/src/app/pages/about/terms-of-service/terms-of-service-page-routing.module.ts
  26. 20
      apps/client/src/app/pages/admin/admin-page-routing.module.ts
  27. 20
      apps/client/src/app/pages/admin/admin-page.component.ts
  28. 2
      apps/client/src/app/pages/admin/admin-page.html
  29. 8
      apps/client/src/app/pages/blog/2021/07/hallo-ghostfolio/hallo-ghostfolio-page.component.ts
  30. 8
      apps/client/src/app/pages/blog/2021/07/hello-ghostfolio/hello-ghostfolio-page.component.ts
  31. 6
      apps/client/src/app/pages/blog/2022/01/first-months-in-open-source/first-months-in-open-source-page.component.ts
  32. 4
      apps/client/src/app/pages/blog/2022/07/ghostfolio-meets-internet-identity/ghostfolio-meets-internet-identity-page.component.ts
  33. 6
      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
  34. 8
      apps/client/src/app/pages/blog/2022/08/500-stars-on-github/500-stars-on-github-page.component.ts
  35. 4
      apps/client/src/app/pages/blog/2022/10/hacktoberfest-2022/hacktoberfest-2022-page.component.ts
  36. 6
      apps/client/src/app/pages/blog/2022/11/black-friday-2022/black-friday-2022-page.component.ts
  37. 4
      apps/client/src/app/pages/blog/2022/12/the-importance-of-tracking-your-personal-finances/the-importance-of-tracking-your-personal-finances-page.component.ts
  38. 4
      apps/client/src/app/pages/blog/2023/01/ghostfolio-auf-sackgeld-vorgestellt/ghostfolio-auf-sackgeld-vorgestellt-page.component.ts
  39. 4
      apps/client/src/app/pages/blog/2023/02/ghostfolio-meets-umbrel/ghostfolio-meets-umbrel-page.component.ts
  40. 6
      apps/client/src/app/pages/blog/2023/03/1000-stars-on-github/1000-stars-on-github-page.component.ts
  41. 6
      apps/client/src/app/pages/blog/2023/05/unlock-your-financial-potential-with-ghostfolio/unlock-your-financial-potential-with-ghostfolio-page.component.ts
  42. 4
      apps/client/src/app/pages/blog/2023/07/exploring-the-path-to-fire/exploring-the-path-to-fire-page.component.ts
  43. 7
      apps/client/src/app/pages/blog/2023/08/ghostfolio-joins-oss-friends/ghostfolio-joins-oss-friends-page.component.ts
  44. 11
      apps/client/src/app/pages/blog/2023/09/ghostfolio-2/ghostfolio-2-page.component.ts
  45. 6
      apps/client/src/app/pages/blog/2023/09/hacktoberfest-2023/hacktoberfest-2023-page.component.ts
  46. 6
      apps/client/src/app/pages/blog/2023/11/black-week-2023/black-week-2023-page.component.ts
  47. 6
      apps/client/src/app/pages/blog/2023/11/hacktoberfest-2023-debriefing/hacktoberfest-2023-debriefing-page.component.ts
  48. 6
      apps/client/src/app/pages/blog/2024/09/hacktoberfest-2024/hacktoberfest-2024-page.component.ts
  49. 6
      apps/client/src/app/pages/blog/2024/11/black-weeks-2024/black-weeks-2024-page.component.ts
  50. 3
      apps/client/src/app/pages/blog/blog-page-routing.module.ts
  51. 8
      apps/client/src/app/pages/faq/faq-page-routing.module.ts
  52. 8
      apps/client/src/app/pages/faq/faq-page.component.ts
  53. 2
      apps/client/src/app/pages/faq/faq-page.html
  54. 4
      apps/client/src/app/pages/faq/overview/faq-overview-page.component.ts
  55. 3
      apps/client/src/app/pages/faq/saas/saas-page-routing.module.ts
  56. 16
      apps/client/src/app/pages/faq/saas/saas-page.component.ts
  57. 3
      apps/client/src/app/pages/faq/self-hosting/self-hosting-page-routing.module.ts
  58. 4
      apps/client/src/app/pages/faq/self-hosting/self-hosting-page.component.ts
  59. 4
      apps/client/src/app/pages/features/features-page.component.ts
  60. 9
      apps/client/src/app/pages/home/home-page-routing.module.ts
  61. 14
      apps/client/src/app/pages/home/home-page.component.ts
  62. 2
      apps/client/src/app/pages/home/home-page.html
  63. 6
      apps/client/src/app/pages/landing/landing-page.component.ts
  64. 3
      apps/client/src/app/pages/markets/markets-page-routing.module.ts
  65. 12
      apps/client/src/app/pages/portfolio/portfolio-page.component.ts
  66. 2
      apps/client/src/app/pages/portfolio/portfolio-page.html
  67. 8
      apps/client/src/app/pages/register/show-access-token-dialog/show-access-token-dialog.component.ts
  68. 4
      apps/client/src/app/pages/resources/glossary/resources-glossary-routing.module.ts
  69. 8
      apps/client/src/app/pages/resources/glossary/resources-glossary.component.ts
  70. 4
      apps/client/src/app/pages/resources/guides/resources-guides-routing.module.ts
  71. 4
      apps/client/src/app/pages/resources/markets/resources-markets-routing.module.ts
  72. 2
      apps/client/src/app/pages/resources/overview/resources-overview.component.html
  73. 18
      apps/client/src/app/pages/resources/overview/resources-overview.component.ts
  74. 8
      apps/client/src/app/pages/resources/personal-finance-tools/personal-finance-tools-page-routing.module.ts
  75. 12
      apps/client/src/app/pages/resources/personal-finance-tools/personal-finance-tools-page.component.ts
  76. 4
      apps/client/src/app/pages/resources/personal-finance-tools/personal-finance-tools-page.html
  77. 10
      apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts
  78. 16
      apps/client/src/app/pages/resources/resources-page-routing.module.ts
  79. 21
      apps/client/src/app/pages/resources/resources-page.component.ts
  80. 2
      apps/client/src/app/pages/resources/resources-page.html
  81. 12
      apps/client/src/app/pages/user-account/user-account-page-routing.module.ts
  82. 13
      apps/client/src/app/pages/user-account/user-account-page.component.ts
  83. 2
      apps/client/src/app/pages/user-account/user-account-page.html
  84. 4
      apps/client/src/app/pages/zen/zen-page.component.ts
  85. 2
      apps/client/src/app/pages/zen/zen-page.html
  86. 530
      apps/client/src/locales/messages.ca.xlf
  87. 526
      apps/client/src/locales/messages.de.xlf
  88. 526
      apps/client/src/locales/messages.es.xlf
  89. 526
      apps/client/src/locales/messages.fr.xlf
  90. 526
      apps/client/src/locales/messages.it.xlf
  91. 526
      apps/client/src/locales/messages.nl.xlf
  92. 526
      apps/client/src/locales/messages.pl.xlf
  93. 552
      apps/client/src/locales/messages.pt.xlf
  94. 526
      apps/client/src/locales/messages.tr.xlf
  95. 530
      apps/client/src/locales/messages.uk.xlf
  96. 522
      apps/client/src/locales/messages.xlf
  97. 550
      apps/client/src/locales/messages.zh.xlf
  98. 2
      libs/common/src/lib/interfaces/tab-configuration.interface.ts
  99. 243
      libs/common/src/lib/routes/routes.ts
  100. 5
      libs/ui/src/lib/account-balances/account-balances.component.ts

15
CHANGELOG.md

@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Unreleased ## Unreleased
### Changed
- Adapted the options of the date range selector in the assistant dynamically based on the user’s first activity
- Migrated the `@ghostfolio/ui/assistant` component to control flow
- Migrated the `@ghostfolio/ui/value` component to control flow
- Improved the language localization for Chinese (`zh`)
- Improved the language localization for Portuguese (`pt`)
## 2.171.0 - 2025-06-15
### Added ### Added
- Added the current holdings as default options of the symbol search in the create or update activity dialog - Added the current holdings as default options of the symbol search in the create or update activity dialog
@ -31,6 +41,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Improved the language localization for Turkish (`tr`) - Improved the language localization for Turkish (`tr`)
- Upgraded the _Stripe_ dependencies - Upgraded the _Stripe_ dependencies
### Fixed
- Fixed a date offset issue with account balances
- Fixed missing `/.well-known/assetlinks.json` for TWA
## 2.170.0 - 2025-06-11 ## 2.170.0 - 2025-06-11
### Added ### Added

8
DEVELOPMENT.md

@ -30,7 +30,13 @@ Run `npm run start:server`
### Start Client ### Start Client
Run `npm run start:client` and open https://localhost:4200/en in your browser #### English (Default)
Run `npm run start:client` and open https://localhost:4200/en in your browser.
#### Other Languages
To start the client in a different language, such as German (`de`), adapt the `start:client` script in the `package.json` file by changing `--configuration=development-en` to `--configuration=development-de`. Then, run `npm run start:client` and open https://localhost:4200/de in your browser.
### Start _Storybook_ ### Start _Storybook_

6
apps/api/src/app/app.module.ts

@ -103,7 +103,7 @@ import { UserModule } from './user/user.module';
RedisCacheModule, RedisCacheModule,
ScheduleModule.forRoot(), ScheduleModule.forRoot(),
ServeStaticModule.forRoot({ ServeStaticModule.forRoot({
exclude: ['/api/*wildcard', '/sitemap.xml'], exclude: ['/.well-known/*wildcard', '/api/*wildcard', '/sitemap.xml'],
rootPath: join(__dirname, '..', 'client'), rootPath: join(__dirname, '..', 'client'),
serveStaticOptions: { serveStaticOptions: {
setHeaders: (res) => { setHeaders: (res) => {
@ -126,6 +126,10 @@ import { UserModule } from './user/user.module';
} }
} }
}), }),
ServeStaticModule.forRoot({
rootPath: join(__dirname, '..', 'client', '.well-known'),
serveRoot: '/.well-known'
}),
SitemapModule, SitemapModule,
SubscriptionModule, SubscriptionModule,
SymbolModule, SymbolModule,

2
apps/api/src/app/endpoints/ai/ai.service.ts

@ -30,7 +30,7 @@ export class AiService {
}); });
const holdingsTable = [ const holdingsTable = [
'| Name | Symbol | Currency | Asset Class | Asset Sub Class | Allocation in Percentage |', '| Name | Symbol | Currency | Asset Class | Asset Sub Class | Allocation in Percentage |',
'| --- | --- | --- | --- | --- | --- |', '| --- | --- | --- | --- | --- | --- |',
...Object.values(holdings) ...Object.values(holdings)
.sort((a, b) => { .sort((a, b) => {

12
apps/api/src/main.ts

@ -1,7 +1,8 @@
import { import {
DEFAULT_HOST, DEFAULT_HOST,
DEFAULT_PORT, DEFAULT_PORT,
STORYBOOK_PATH STORYBOOK_PATH,
SUPPORTED_LANGUAGE_CODES
} from '@ghostfolio/common/config'; } from '@ghostfolio/common/config';
import { import {
@ -43,7 +44,14 @@ async function bootstrap() {
defaultVersion: '1', defaultVersion: '1',
type: VersioningType.URI type: VersioningType.URI
}); });
app.setGlobalPrefix('api', { exclude: ['sitemap.xml'] }); app.setGlobalPrefix('api', {
exclude: [
'sitemap.xml',
...SUPPORTED_LANGUAGE_CODES.map((languageCode) => {
return `/${languageCode}/*wildcard`;
})
]
});
app.useGlobalPipes( app.useGlobalPipes(
new ValidationPipe({ new ValidationPipe({
forbidNonWhitelisted: true, forbidNonWhitelisted: true,

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

@ -1,10 +1,6 @@
import { AuthGuard } from '@ghostfolio/client/core/auth.guard'; import { AuthGuard } from '@ghostfolio/client/core/auth.guard';
import { PageTitleStrategy } from '@ghostfolio/client/services/page-title.strategy'; import { PageTitleStrategy } from '@ghostfolio/client/services/page-title.strategy';
import { import { publicRoutes, internalRoutes } from '@ghostfolio/common/routes/routes';
publicRoutes,
routes as ghostfolioRoutes,
internalRoutes
} from '@ghostfolio/common/routes/routes';
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { RouterModule, Routes, TitleStrategy } from '@angular/router'; import { RouterModule, Routes, TitleStrategy } from '@angular/router';
@ -13,7 +9,7 @@ import { ModulePreloadService } from './core/module-preload.service';
const routes: Routes = [ const routes: Routes = [
{ {
path: ghostfolioRoutes.about, path: publicRoutes.about.path,
loadChildren: () => loadChildren: () =>
import('./pages/about/about-page.module').then((m) => m.AboutPageModule) import('./pages/about/about-page.module').then((m) => m.AboutPageModule)
}, },
@ -32,7 +28,7 @@ const routes: Routes = [
) )
}, },
{ {
path: ghostfolioRoutes.adminControl, path: internalRoutes.adminControl.path,
loadChildren: () => loadChildren: () =>
import('./pages/admin/admin-page.module').then((m) => m.AdminPageModule) import('./pages/admin/admin-page.module').then((m) => m.AdminPageModule)
}, },
@ -42,16 +38,17 @@ const routes: Routes = [
import('./pages/api/api-page.component').then( import('./pages/api/api-page.component').then(
(c) => c.GfApiPageComponent (c) => c.GfApiPageComponent
), ),
path: ghostfolioRoutes.api, path: internalRoutes.api.path,
title: 'Ghostfolio API' title: internalRoutes.api.title
}, },
{ {
path: ghostfolioRoutes.auth, path: internalRoutes.auth.path,
loadChildren: () => loadChildren: () =>
import('./pages/auth/auth-page.module').then((m) => m.AuthPageModule) import('./pages/auth/auth-page.module').then((m) => m.AuthPageModule),
title: internalRoutes.auth.title
}, },
{ {
path: ghostfolioRoutes.blog, path: publicRoutes.blog.path,
loadChildren: () => loadChildren: () =>
import('./pages/blog/blog-page.module').then((m) => m.BlogPageModule) import('./pages/blog/blog-page.module').then((m) => m.BlogPageModule)
}, },
@ -61,10 +58,10 @@ const routes: Routes = [
import('./pages/demo/demo-page.component').then( import('./pages/demo/demo-page.component').then(
(c) => c.GfDemoPageComponent (c) => c.GfDemoPageComponent
), ),
path: ghostfolioRoutes.demo path: publicRoutes.demo.path
}, },
{ {
path: ghostfolioRoutes.faq, path: publicRoutes.faq.path,
loadChildren: () => loadChildren: () =>
import('./pages/faq/faq-page.module').then((m) => m.FaqPageModule) import('./pages/faq/faq-page.module').then((m) => m.FaqPageModule)
}, },
@ -88,11 +85,11 @@ const routes: Routes = [
import('./pages/i18n/i18n-page.component').then( import('./pages/i18n/i18n-page.component').then(
(c) => c.GfI18nPageComponent (c) => c.GfI18nPageComponent
), ),
path: ghostfolioRoutes.i18n, path: internalRoutes.i18n.path,
title: $localize`Internationalization` title: internalRoutes.i18n.title
}, },
{ {
path: ghostfolioRoutes.markets, path: publicRoutes.markets.path,
loadChildren: () => loadChildren: () =>
import('./pages/markets/markets-page.module').then( import('./pages/markets/markets-page.module').then(
(m) => m.MarketsPageModule (m) => m.MarketsPageModule
@ -111,14 +108,14 @@ const routes: Routes = [
) )
}, },
{ {
path: ghostfolioRoutes.pricing, path: publicRoutes.pricing.path,
loadChildren: () => loadChildren: () =>
import('./pages/pricing/pricing-page.module').then( import('./pages/pricing/pricing-page.module').then(
(m) => m.PricingPageModule (m) => m.PricingPageModule
) )
}, },
{ {
path: ghostfolioRoutes.public, path: publicRoutes.public.path,
loadChildren: () => loadChildren: () =>
import('./pages/public/public-page.module').then( import('./pages/public/public-page.module').then(
(m) => m.PublicPageModule (m) => m.PublicPageModule
@ -132,14 +129,14 @@ const routes: Routes = [
) )
}, },
{ {
path: ghostfolioRoutes.resources, path: publicRoutes.resources.path,
loadChildren: () => loadChildren: () =>
import('./pages/resources/resources-page.module').then( import('./pages/resources/resources-page.module').then(
(m) => m.ResourcesPageModule (m) => m.ResourcesPageModule
) )
}, },
{ {
path: ghostfolioRoutes.start, path: publicRoutes.start.path,
loadChildren: () => loadChildren: () =>
import('./pages/landing/landing-page.module').then( import('./pages/landing/landing-page.module').then(
(m) => m.LandingPageModule (m) => m.LandingPageModule
@ -150,8 +147,8 @@ const routes: Routes = [
import('./pages/webauthn/webauthn-page.component').then( import('./pages/webauthn/webauthn-page.component').then(
(c) => c.GfWebauthnPageComponent (c) => c.GfWebauthnPageComponent
), ),
path: ghostfolioRoutes.webauthn, path: internalRoutes.webauthn.path,
title: $localize`Sign in` title: internalRoutes.webauthn.title
}, },
{ {
path: internalRoutes.zen.path, path: internalRoutes.zen.path,

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

@ -3,11 +3,7 @@ import { HoldingDetailDialogParams } from '@ghostfolio/client/components/holding
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 { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import { import { internalRoutes, publicRoutes } from '@ghostfolio/common/routes/routes';
internalRoutes,
publicRoutes,
routes
} from '@ghostfolio/common/routes/routes';
import { ColorScheme } from '@ghostfolio/common/types'; import { ColorScheme } from '@ghostfolio/common/types';
import { DOCUMENT } from '@angular/common'; import { DOCUMENT } from '@angular/common';
@ -67,25 +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 = ['/' + routes.about]; public routerLinkAbout = publicRoutes.about.routerLink;
public routerLinkAboutChangelog = ['/' + routes.about, routes.changelog]; public routerLinkAboutChangelog =
public routerLinkAboutLicense = ['/' + routes.about, routes.license]; publicRoutes.about.subRoutes.changelog.routerLink;
public routerLinkAboutPrivacyPolicy = [ public routerLinkAboutLicense =
'/' + routes.about, publicRoutes.about.subRoutes.license.routerLink;
routes.privacyPolicy public routerLinkAboutPrivacyPolicy =
]; publicRoutes.about.subRoutes.privacyPolicy.routerLink;
public routerLinkAboutTermsOfService = [ public routerLinkAboutTermsOfService =
'/' + routes.about, publicRoutes.about.subRoutes.termsOfService.routerLink;
routes.termsOfService public routerLinkBlog = publicRoutes.blog.routerLink;
]; public routerLinkFaq = publicRoutes.faq.routerLink;
public routerLinkBlog = ['/' + routes.blog];
public routerLinkFaq = ['/' + routes.faq];
public routerLinkFeatures = publicRoutes.features.routerLink; public routerLinkFeatures = publicRoutes.features.routerLink;
public routerLinkMarkets = ['/' + routes.markets]; public routerLinkMarkets = publicRoutes.markets.routerLink;
public routerLinkOpenStartup = publicRoutes.openStartup.routerLink; public routerLinkOpenStartup = publicRoutes.openStartup.routerLink;
public routerLinkPricing = ['/' + routes.pricing]; public routerLinkPricing = publicRoutes.pricing.routerLink;
public routerLinkRegister = publicRoutes.register.routerLink; public routerLinkRegister = publicRoutes.register.routerLink;
public routerLinkResources = ['/' + routes.resources]; public routerLinkResources = publicRoutes.resources.routerLink;
public showFooter = false; public showFooter = false;
public user: User; public user: User;
@ -200,25 +194,25 @@ export class AppComponent implements OnDestroy, OnInit {
} }
this.hasTabs = this.hasTabs =
(this.currentRoute === routes.about || (this.currentRoute === publicRoutes.about.path ||
this.currentRoute === routes.faq || this.currentRoute === publicRoutes.faq.path ||
this.currentRoute === routes.resources || this.currentRoute === publicRoutes.resources.path ||
this.currentRoute === internalRoutes.account.path || this.currentRoute === internalRoutes.account.path ||
this.currentRoute === routes.adminControl || this.currentRoute === internalRoutes.adminControl.path ||
this.currentRoute === internalRoutes.home.path || this.currentRoute === internalRoutes.home.path ||
this.currentRoute === internalRoutes.portfolio.path || this.currentRoute === internalRoutes.portfolio.path ||
this.currentRoute === internalRoutes.zen.path) && this.currentRoute === internalRoutes.zen.path) &&
this.deviceType !== 'mobile'; this.deviceType !== 'mobile';
this.showFooter = this.showFooter =
(this.currentRoute === routes.blog || (this.currentRoute === publicRoutes.blog.path ||
this.currentRoute === publicRoutes.features.path || this.currentRoute === publicRoutes.features.path ||
this.currentRoute === routes.markets || this.currentRoute === publicRoutes.markets.path ||
this.currentRoute === publicRoutes.openStartup.path || this.currentRoute === publicRoutes.openStartup.path ||
this.currentRoute === routes.public || this.currentRoute === publicRoutes.public.path ||
this.currentRoute === routes.pricing || this.currentRoute === publicRoutes.pricing.path ||
this.currentRoute === publicRoutes.register.path || this.currentRoute === publicRoutes.register.path ||
this.currentRoute === routes.start) && this.currentRoute === publicRoutes.start.path) &&
this.deviceType !== 'mobile'; this.deviceType !== 'mobile';
if (this.deviceType === 'mobile') { if (this.deviceType === 'mobile') {

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

@ -1,7 +1,7 @@
import { ConfirmationDialogType } from '@ghostfolio/client/core/notification/confirmation-dialog/confirmation-dialog.type'; import { ConfirmationDialogType } from '@ghostfolio/client/core/notification/confirmation-dialog/confirmation-dialog.type';
import { NotificationService } from '@ghostfolio/client/core/notification/notification.service'; import { NotificationService } from '@ghostfolio/client/core/notification/notification.service';
import { Access, User } from '@ghostfolio/common/interfaces'; import { Access, User } from '@ghostfolio/common/interfaces';
import { routes } from '@ghostfolio/common/routes/routes'; import { publicRoutes } from '@ghostfolio/common/routes/routes';
import { Clipboard } from '@angular/cdk/clipboard'; import { Clipboard } from '@angular/cdk/clipboard';
import { import {
@ -55,7 +55,7 @@ export class AccessTableComponent implements OnChanges {
public getPublicUrl(aId: string): string { public getPublicUrl(aId: string): string {
const languageCode = this.user.settings.language; const languageCode = this.user.settings.language;
return `${this.baseUrl}/${languageCode}/${routes.public}/${aId}`; return `${this.baseUrl}/${languageCode}/${publicRoutes.public.path}/${aId}`;
} }
public onCopyUrlToClipboard(aId: string): void { public onCopyUrlToClipboard(aId: string): void {

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

@ -10,7 +10,7 @@ import {
DataProviderInfo, DataProviderInfo,
User User
} from '@ghostfolio/common/interfaces'; } from '@ghostfolio/common/interfaces';
import { routes } from '@ghostfolio/common/routes/routes'; import { publicRoutes } from '@ghostfolio/common/routes/routes';
import { import {
ChangeDetectionStrategy, ChangeDetectionStrategy,
@ -72,7 +72,7 @@ export class AdminSettingsComponent implements OnDestroy, OnInit {
const languageCode = this.user.settings.language; const languageCode = this.user.settings.language;
this.pricingUrl = `https://ghostfol.io/${languageCode}/${routes.pricing}`; this.pricingUrl = `https://ghostfol.io/${languageCode}/${publicRoutes.pricing.path}`;
this.changeDetectorRef.markForCheck(); this.changeDetectorRef.markForCheck();
} }

8
apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.ts

@ -13,7 +13,7 @@ import {
} from '@ghostfolio/common/helper'; } from '@ghostfolio/common/helper';
import { LineChartItem, User } from '@ghostfolio/common/interfaces'; import { LineChartItem, User } from '@ghostfolio/common/interfaces';
import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import { routes } from '@ghostfolio/common/routes/routes'; import { internalRoutes } from '@ghostfolio/common/routes/routes';
import { ColorScheme } from '@ghostfolio/common/types'; import { ColorScheme } from '@ghostfolio/common/types';
import { import {
@ -64,10 +64,8 @@ export class BenchmarkComparatorComponent implements OnChanges, OnDestroy {
public chart: Chart<'line'>; public chart: Chart<'line'>;
public hasPermissionToAccessAdminControl: boolean; public hasPermissionToAccessAdminControl: boolean;
public routerLinkAdminControlMarketData = [ public routerLinkAdminControlMarketData =
'/' + routes.adminControl, internalRoutes.adminControl.subRoutes.marketData.routerLink;
routes.marketData
];
public constructor() { public constructor() {
Chart.register( Chart.register(

13
apps/client/src/app/components/header/header.component.html

@ -65,8 +65,10 @@
i18n i18n
mat-flat-button mat-flat-button
[ngClass]="{ [ngClass]="{
'font-weight-bold': currentRoute === routes.adminControl, 'font-weight-bold':
'text-decoration-underline': currentRoute === routes.adminControl currentRoute === internalRoutes.adminControl.path,
'text-decoration-underline':
currentRoute === internalRoutes.adminControl.path
}" }"
[routerLink]="routerLinkAdminControl" [routerLink]="routerLinkAdminControl"
>Admin Control</a >Admin Control</a
@ -268,7 +270,9 @@
<a <a
i18n i18n
mat-menu-item mat-menu-item
[ngClass]="{ 'font-weight-bold': currentRoute === routes.account }" [ngClass]="{
'font-weight-bold': currentRoute === internalRoutes.account.path
}"
[routerLink]="routerLinkAccount" [routerLink]="routerLinkAccount"
>My Ghostfolio</a >My Ghostfolio</a
> >
@ -278,7 +282,8 @@
i18n i18n
mat-menu-item mat-menu-item
[ngClass]="{ [ngClass]="{
'font-weight-bold': currentRoute === routes.adminControl 'font-weight-bold':
currentRoute === internalRoutes.adminControl.path
}" }"
[routerLink]="routerLinkAdminControl" [routerLink]="routerLinkAdminControl"
>Admin Control</a >Admin Control</a

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

@ -12,11 +12,7 @@ import { TokenStorageService } from '@ghostfolio/client/services/token-storage.s
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 { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import { import { internalRoutes, publicRoutes } from '@ghostfolio/common/routes/routes';
internalRoutes,
publicRoutes,
routes
} from '@ghostfolio/common/routes/routes';
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';
@ -85,22 +81,21 @@ export class HeaderComponent implements OnChanges {
public impersonationId: string; public impersonationId: string;
public internalRoutes = internalRoutes; public internalRoutes = internalRoutes;
public isMenuOpen: boolean; public isMenuOpen: boolean;
public routeAbout = routes.about; public routeAbout = publicRoutes.about.path;
public routeFeatures = publicRoutes.features.path; public routeFeatures = publicRoutes.features.path;
public routeMarkets = routes.markets; public routeMarkets = publicRoutes.markets.path;
public routePricing = routes.pricing; public routePricing = publicRoutes.pricing.path;
public routeResources = routes.resources; public routeResources = publicRoutes.resources.path;
public routerLinkAbout = ['/' + routes.about]; public routerLinkAbout = publicRoutes.about.routerLink;
public routerLinkAccount = internalRoutes.account.routerLink; public routerLinkAccount = internalRoutes.account.routerLink;
public routerLinkAccounts = internalRoutes.accounts.routerLink; public routerLinkAccounts = internalRoutes.accounts.routerLink;
public routerLinkAdminControl = ['/' + routes.adminControl]; public routerLinkAdminControl = internalRoutes.adminControl.routerLink;
public routerLinkFeatures = publicRoutes.features.routerLink; public routerLinkFeatures = publicRoutes.features.routerLink;
public routerLinkMarkets = ['/' + routes.markets]; public routerLinkMarkets = publicRoutes.markets.routerLink;
public routerLinkPortfolio = internalRoutes.portfolio.routerLink; public routerLinkPortfolio = internalRoutes.portfolio.routerLink;
public routerLinkPricing = ['/' + routes.pricing]; public routerLinkPricing = publicRoutes.pricing.routerLink;
public routerLinkRegister = publicRoutes.register.routerLink; public routerLinkRegister = publicRoutes.register.routerLink;
public routerLinkResources = ['/' + routes.resources]; public routerLinkResources = publicRoutes.resources.routerLink;
public routes = routes;
private unsubscribeSubject = new Subject<void>(); private unsubscribeSubject = new Subject<void>();

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

@ -1,4 +1,4 @@
import { routes } from '@ghostfolio/common/routes/routes'; import { publicRoutes } from '@ghostfolio/common/routes/routes';
import { import {
ChangeDetectionStrategy, ChangeDetectionStrategy,
@ -28,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 = ['/' + routes.pricing]; public routerLinkPricing = publicRoutes.pricing.routerLink;
public variantIndex: number; public variantIndex: number;
private unsubscribeSubject = new Subject<void>(); private unsubscribeSubject = new Subject<void>();

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

@ -5,7 +5,7 @@ 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 { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import { routes } from '@ghostfolio/common/routes/routes'; import { publicRoutes } from '@ghostfolio/common/routes/routes';
import { import {
ChangeDetectionStrategy, ChangeDetectionStrategy,
@ -37,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 = ['/' + routes.pricing]; public routerLinkPricing = publicRoutes.pricing.routerLink;
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;

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

@ -1,11 +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 { import { internalRoutes, publicRoutes } from '@ghostfolio/common/routes/routes';
internalRoutes,
publicRoutes,
routes
} from '@ghostfolio/common/routes/routes';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { import {
@ -19,17 +15,17 @@ import { catchError } from 'rxjs/operators';
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class AuthGuard { export class AuthGuard {
private static PUBLIC_PAGE_ROUTES = [ private static PUBLIC_PAGE_ROUTES = [
`/${routes.about}`, `/${publicRoutes.about.path}`,
`/${routes.blog}`, `/${publicRoutes.blog.path}`,
`/${routes.demo}`, `/${publicRoutes.demo.path}`,
`/${routes.faq}`, `/${publicRoutes.faq.path}`,
`/${publicRoutes.features.path}`, `/${publicRoutes.features.path}`,
`/${routes.markets}`, `/${publicRoutes.markets.path}`,
`/${publicRoutes.openStartup.path}`, `/${publicRoutes.openStartup.path}`,
`/${routes.pricing}`, `/${publicRoutes.pricing.path}`,
`/${routes.public}`, `/${publicRoutes.public.path}`,
`/${publicRoutes.register.path}`, `/${publicRoutes.register.path}`,
`/${routes.resources}` `/${publicRoutes.resources.path}`
]; ];
public constructor( public constructor(
@ -52,7 +48,7 @@ export class AuthGuard {
.pipe( .pipe(
catchError(() => { catchError(() => {
if (utmSource === 'ios') { if (utmSource === 'ios') {
this.router.navigate(['/' + routes.demo]); this.router.navigate(publicRoutes.demo.routerLink);
resolve(false); resolve(false);
} else if (utmSource === 'trusted-web-activity') { } else if (utmSource === 'trusted-web-activity') {
this.router.navigate(publicRoutes.register.routerLink); this.router.navigate(publicRoutes.register.routerLink);
@ -66,7 +62,7 @@ export class AuthGuard {
resolve(true); resolve(true);
return EMPTY; return EMPTY;
} else if (state.url !== '/start') { } else if (state.url !== '/start') {
this.router.navigate(['/' + routes.start]); this.router.navigate(publicRoutes.start.routerLink);
resolve(false); resolve(false);
return EMPTY; return EMPTY;
} }
@ -98,7 +94,7 @@ export class AuthGuard {
this.router.navigate(internalRoutes.zen.routerLink); this.router.navigate(internalRoutes.zen.routerLink);
resolve(false); resolve(false);
return; return;
} else if (state.url.startsWith(`/${routes.start}`)) { } else if (state.url.startsWith(`/${publicRoutes.start.path}`)) {
if (user.settings.viewMode === 'ZEN') { if (user.settings.viewMode === 'ZEN') {
this.router.navigate(internalRoutes.zen.routerLink); this.router.navigate(internalRoutes.zen.routerLink);
} else { } else {

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

@ -2,7 +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 { routes } from '@ghostfolio/common/routes/routes'; import { internalRoutes, publicRoutes } from '@ghostfolio/common/routes/routes';
import { import {
HTTP_INTERCEPTORS, HTTP_INTERCEPTORS,
@ -60,7 +60,9 @@ export class HttpResponseInterceptor implements HttpInterceptor {
duration: ms('6 seconds') duration: ms('6 seconds')
} }
); );
} else if (!error.url.includes('/auth')) { } else if (
!error.url.includes(internalRoutes.auth.routerLink.join(''))
) {
this.snackBarRef = this.snackBar.open( this.snackBarRef = this.snackBar.open(
$localize`This action is not allowed.`, $localize`This action is not allowed.`,
undefined, undefined,
@ -75,7 +77,7 @@ export class HttpResponseInterceptor implements HttpInterceptor {
}); });
this.snackBarRef.onAction().subscribe(() => { this.snackBarRef.onAction().subscribe(() => {
this.router.navigate(['/' + routes.pricing]); this.router.navigate(publicRoutes.pricing.routerLink);
}); });
} }
} else if (error.status === StatusCodes.INTERNAL_SERVER_ERROR) { } else if (error.status === StatusCodes.INTERNAL_SERVER_ERROR) {
@ -111,7 +113,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(['/' + routes.webauthn]); this.router.navigate(internalRoutes.webauthn.routerLink);
} else { } else {
this.tokenStorageService.signOut(); this.tokenStorageService.signOut();
} }

14
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 { routes as ghostfolioRoutes } from '@ghostfolio/common/routes/routes'; import { publicRoutes } from '@ghostfolio/common/routes/routes';
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router';
@ -18,35 +18,35 @@ const routes: Routes = [
) )
}, },
{ {
path: ghostfolioRoutes.changelog, path: publicRoutes.about.subRoutes.changelog.path,
loadChildren: () => loadChildren: () =>
import('./changelog/changelog-page.module').then( import('./changelog/changelog-page.module').then(
(m) => m.ChangelogPageModule (m) => m.ChangelogPageModule
) )
}, },
{ {
path: ghostfolioRoutes.license, path: publicRoutes.about.subRoutes.license.path,
loadChildren: () => loadChildren: () =>
import('./license/license-page.module').then( import('./license/license-page.module').then(
(m) => m.LicensePageModule (m) => m.LicensePageModule
) )
}, },
{ {
path: ghostfolioRoutes.ossFriends, path: publicRoutes.about.subRoutes.ossFriends.path,
loadChildren: () => loadChildren: () =>
import('./oss-friends/oss-friends-page.module').then( import('./oss-friends/oss-friends-page.module').then(
(m) => m.OpenSourceSoftwareFriendsPageModule (m) => m.OpenSourceSoftwareFriendsPageModule
) )
}, },
{ {
path: ghostfolioRoutes.privacyPolicy, path: publicRoutes.about.subRoutes.privacyPolicy.path,
loadChildren: () => loadChildren: () =>
import('./privacy-policy/privacy-policy-page.module').then( import('./privacy-policy/privacy-policy-page.module').then(
(m) => m.PrivacyPolicyPageModule (m) => m.PrivacyPolicyPageModule
) )
}, },
{ {
path: ghostfolioRoutes.termsOfService, path: publicRoutes.about.subRoutes.termsOfService.path,
loadChildren: () => loadChildren: () =>
import('./terms-of-service/terms-of-service-page.module').then( import('./terms-of-service/terms-of-service-page.module').then(
(m) => m.TermsOfServicePageModule (m) => m.TermsOfServicePageModule
@ -55,7 +55,7 @@ const routes: Routes = [
], ],
component: AboutPageComponent, component: AboutPageComponent,
path: '', path: '',
title: $localize`About` title: publicRoutes.about.title
} }
]; ];

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

@ -2,7 +2,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 { TabConfiguration, User } from '@ghostfolio/common/interfaces'; import { TabConfiguration, User } from '@ghostfolio/common/interfaces';
import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import { routes } from '@ghostfolio/common/routes/routes'; import { publicRoutes } from '@ghostfolio/common/routes/routes';
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';
@ -43,18 +43,18 @@ export class AboutPageComponent implements OnDestroy, OnInit {
this.tabs = [ this.tabs = [
{ {
iconName: 'information-circle-outline', iconName: 'information-circle-outline',
label: $localize`About`, label: publicRoutes.about.title,
path: ['/' + routes.about] routerLink: publicRoutes.about.routerLink
}, },
{ {
iconName: 'sparkles-outline', iconName: 'sparkles-outline',
label: $localize`Changelog`, label: publicRoutes.about.subRoutes.changelog.title,
path: ['/' + routes.about, routes.changelog] routerLink: publicRoutes.about.subRoutes.changelog.routerLink
}, },
{ {
iconName: 'ribbon-outline', iconName: 'ribbon-outline',
label: $localize`License`, label: publicRoutes.about.subRoutes.license.title,
path: ['/' + routes.about, routes.license], routerLink: publicRoutes.about.subRoutes.license.routerLink,
showCondition: !this.hasPermissionForSubscription showCondition: !this.hasPermissionForSubscription
} }
]; ];
@ -62,15 +62,15 @@ export class AboutPageComponent implements OnDestroy, OnInit {
if (state?.user) { if (state?.user) {
this.tabs.push({ this.tabs.push({
iconName: 'shield-checkmark-outline', iconName: 'shield-checkmark-outline',
label: $localize`Privacy Policy`, label: publicRoutes.about.subRoutes.privacyPolicy.title,
path: ['/' + routes.about, routes.privacyPolicy], routerLink: publicRoutes.about.subRoutes.privacyPolicy.routerLink,
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: publicRoutes.about.subRoutes.termsOfService.title,
path: ['/' + routes.about, routes.termsOfService], routerLink: publicRoutes.about.subRoutes.termsOfService.routerLink,
showCondition: this.hasPermissionForSubscription showCondition: this.hasPermissionForSubscription
}); });
@ -81,8 +81,8 @@ export class AboutPageComponent implements OnDestroy, OnInit {
this.tabs.push({ this.tabs.push({
iconName: 'happy-outline', iconName: 'happy-outline',
label: 'OSS Friends', label: publicRoutes.about.subRoutes.ossFriends.title,
path: ['/' + routes.about, routes.ossFriends] routerLink: publicRoutes.about.subRoutes.ossFriends.routerLink
}); });
}); });
} }

2
apps/client/src/app/pages/about/about-page.html

@ -16,7 +16,7 @@
mat-tab-link mat-tab-link
routerLinkActive routerLinkActive
[active]="rla.isActive" [active]="rla.isActive"
[routerLink]="tab.path" [routerLink]="tab.routerLink"
[routerLinkActiveOptions]="{ exact: true }" [routerLinkActiveOptions]="{ exact: true }"
> >
<ion-icon <ion-icon

3
apps/client/src/app/pages/about/changelog/changelog-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 { publicRoutes } from '@ghostfolio/common/routes/routes';
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router';
@ -10,7 +11,7 @@ const routes: Routes = [
canActivate: [AuthGuard], canActivate: [AuthGuard],
component: ChangelogPageComponent, component: ChangelogPageComponent,
path: '', path: '',
title: $localize`Changelog` title: publicRoutes.about.subRoutes.changelog.title
} }
]; ];

3
apps/client/src/app/pages/about/license/license-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 { publicRoutes } from '@ghostfolio/common/routes/routes';
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router';
@ -10,7 +11,7 @@ const routes: Routes = [
canActivate: [AuthGuard], canActivate: [AuthGuard],
component: LicensePageComponent, component: LicensePageComponent,
path: '', path: '',
title: $localize`License` title: publicRoutes.about.subRoutes.license.title
} }
]; ];

3
apps/client/src/app/pages/about/oss-friends/oss-friends-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 { publicRoutes } from '@ghostfolio/common/routes/routes';
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router';
@ -10,7 +11,7 @@ const routes: Routes = [
canActivate: [AuthGuard], canActivate: [AuthGuard],
component: OpenSourceSoftwareFriendsPageComponent, component: OpenSourceSoftwareFriendsPageComponent,
path: '', path: '',
title: 'OSS Friends' title: publicRoutes.about.subRoutes.ossFriends.title
} }
]; ];

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

@ -2,7 +2,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 { User } from '@ghostfolio/common/interfaces'; import { User } from '@ghostfolio/common/interfaces';
import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import { publicRoutes, routes } from '@ghostfolio/common/routes/routes'; import { publicRoutes } from '@ghostfolio/common/routes/routes';
import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
@ -18,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 routerLinkBlog = ['/' + routes.blog]; public routerLinkBlog = publicRoutes.blog.routerLink;
public routerLinkFaq = ['/' + routes.faq]; public routerLinkFaq = publicRoutes.faq.routerLink;
public routerLinkFeatures = publicRoutes.features.routerLink; public routerLinkFeatures = publicRoutes.features.routerLink;
public routerLinkOpenStartup = publicRoutes.openStartup.routerLink; public routerLinkOpenStartup = publicRoutes.openStartup.routerLink;
public user: User; public user: User;

3
apps/client/src/app/pages/about/privacy-policy/privacy-policy-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 { publicRoutes } from '@ghostfolio/common/routes/routes';
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router';
@ -10,7 +11,7 @@ const routes: Routes = [
canActivate: [AuthGuard], canActivate: [AuthGuard],
component: PrivacyPolicyPageComponent, component: PrivacyPolicyPageComponent,
path: '', path: '',
title: $localize`Privacy Policy` title: publicRoutes.about.subRoutes.privacyPolicy.title
} }
]; ];

3
apps/client/src/app/pages/about/terms-of-service/terms-of-service-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 { publicRoutes } from '@ghostfolio/common/routes/routes';
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router';
@ -10,7 +11,7 @@ const routes: Routes = [
canActivate: [AuthGuard], canActivate: [AuthGuard],
component: TermsOfServicePageComponent, component: TermsOfServicePageComponent,
path: '', path: '',
title: $localize`Terms of Service` title: publicRoutes.about.subRoutes.termsOfService.title
} }
]; ];

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

@ -4,7 +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 { routes as ghostfolioRoutes } from '@ghostfolio/common/routes/routes'; import { internalRoutes } from '@ghostfolio/common/routes/routes';
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router';
@ -18,27 +18,27 @@ const routes: Routes = [
{ {
path: '', path: '',
component: AdminOverviewComponent, component: AdminOverviewComponent,
title: $localize`Admin Control` title: internalRoutes.adminControl.title
}, },
{ {
path: ghostfolioRoutes.jobs, path: internalRoutes.adminControl.subRoutes.jobs.path,
component: AdminJobsComponent, component: AdminJobsComponent,
title: $localize`Job Queue` title: internalRoutes.adminControl.subRoutes.jobs.title
}, },
{ {
path: ghostfolioRoutes.marketData, path: internalRoutes.adminControl.subRoutes.marketData.path,
component: AdminMarketDataComponent, component: AdminMarketDataComponent,
title: $localize`Market Data` title: internalRoutes.adminControl.subRoutes.marketData.title
}, },
{ {
path: ghostfolioRoutes.settings, path: internalRoutes.adminControl.subRoutes.settings.path,
component: AdminSettingsComponent, component: AdminSettingsComponent,
title: $localize`Settings` title: internalRoutes.adminControl.subRoutes.settings.title
}, },
{ {
path: ghostfolioRoutes.users, path: internalRoutes.adminControl.subRoutes.users.path,
component: AdminUsersComponent, component: AdminUsersComponent,
title: $localize`Users` title: internalRoutes.adminControl.subRoutes.users.title
} }
], ],
component: AdminPageComponent, component: AdminPageComponent,

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

@ -1,5 +1,5 @@
import { TabConfiguration } from '@ghostfolio/common/interfaces'; import { TabConfiguration } from '@ghostfolio/common/interfaces';
import { routes } from '@ghostfolio/common/routes/routes'; import { internalRoutes } from '@ghostfolio/common/routes/routes';
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';
@ -27,27 +27,27 @@ export class AdminPageComponent implements OnDestroy, OnInit {
{ {
iconName: 'reader-outline', iconName: 'reader-outline',
label: $localize`Overview`, label: $localize`Overview`,
path: ['/' + routes.adminControl] routerLink: internalRoutes.adminControl.routerLink
}, },
{ {
iconName: 'settings-outline', iconName: 'settings-outline',
label: $localize`Settings`, label: internalRoutes.adminControl.subRoutes.settings.title,
path: ['/' + routes.adminControl, routes.settings] routerLink: internalRoutes.adminControl.subRoutes.settings.routerLink
}, },
{ {
iconName: 'server-outline', iconName: 'server-outline',
label: $localize`Market Data`, label: internalRoutes.adminControl.subRoutes.marketData.title,
path: ['/' + routes.adminControl, routes.marketData] routerLink: internalRoutes.adminControl.subRoutes.marketData.routerLink
}, },
{ {
iconName: 'flash-outline', iconName: 'flash-outline',
label: $localize`Job Queue`, label: internalRoutes.adminControl.subRoutes.jobs.title,
path: ['/' + routes.adminControl, routes.jobs] routerLink: internalRoutes.adminControl.subRoutes.jobs.routerLink
}, },
{ {
iconName: 'people-outline', iconName: 'people-outline',
label: $localize`Users`, label: internalRoutes.adminControl.subRoutes.users.title,
path: ['/' + routes.adminControl, routes.users] routerLink: internalRoutes.adminControl.subRoutes.users.routerLink
} }
]; ];
} }

2
apps/client/src/app/pages/admin/admin-page.html

@ -16,7 +16,7 @@
mat-tab-link mat-tab-link
routerLinkActive routerLinkActive
[active]="rla.isActive" [active]="rla.isActive"
[routerLink]="tab.path" [routerLink]="tab.routerLink"
[routerLinkActiveOptions]="{ exact: true }" [routerLinkActiveOptions]="{ exact: true }"
> >
<ion-icon <ion-icon

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

@ -1,4 +1,4 @@
import { routes } from '@ghostfolio/common/routes/routes'; import { publicRoutes } from '@ghostfolio/common/routes/routes';
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
@ -11,7 +11,7 @@ import { RouterModule } from '@angular/router';
templateUrl: './hallo-ghostfolio-page.html' templateUrl: './hallo-ghostfolio-page.html'
}) })
export class HalloGhostfolioPageComponent { export class HalloGhostfolioPageComponent {
public routerLinkBlog = ['/' + routes.blog]; public routerLinkBlog = publicRoutes.blog.routerLink;
public routerLinkPricing = ['/' + routes.pricing]; public routerLinkPricing = publicRoutes.pricing.routerLink;
public routerLinkResources = ['/' + routes.resources]; public routerLinkResources = publicRoutes.resources.routerLink;
} }

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

@ -1,4 +1,4 @@
import { routes } from '@ghostfolio/common/routes/routes'; import { publicRoutes } from '@ghostfolio/common/routes/routes';
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
@ -11,7 +11,7 @@ import { RouterModule } from '@angular/router';
templateUrl: './hello-ghostfolio-page.html' templateUrl: './hello-ghostfolio-page.html'
}) })
export class HelloGhostfolioPageComponent { export class HelloGhostfolioPageComponent {
public routerLinkBlog = ['/' + routes.blog]; public routerLinkBlog = publicRoutes.blog.routerLink;
public routerLinkPricing = ['/' + routes.pricing]; public routerLinkPricing = publicRoutes.pricing.routerLink;
public routerLinkResources = ['/' + routes.resources]; public routerLinkResources = publicRoutes.resources.routerLink;
} }

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

@ -1,4 +1,4 @@
import { routes } from '@ghostfolio/common/routes/routes'; import { publicRoutes } from '@ghostfolio/common/routes/routes';
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
@ -11,6 +11,6 @@ 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 routerLinkBlog = ['/' + routes.blog]; public routerLinkBlog = publicRoutes.blog.routerLink;
public routerLinkPricing = ['/' + routes.pricing]; public routerLinkPricing = publicRoutes.pricing.routerLink;
} }

4
apps/client/src/app/pages/blog/2022/07/ghostfolio-meets-internet-identity/ghostfolio-meets-internet-identity-page.component.ts

@ -1,4 +1,4 @@
import { routes } from '@ghostfolio/common/routes/routes'; import { publicRoutes } from '@ghostfolio/common/routes/routes';
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
@ -11,5 +11,5 @@ import { RouterModule } from '@angular/router';
templateUrl: './ghostfolio-meets-internet-identity-page.html' templateUrl: './ghostfolio-meets-internet-identity-page.html'
}) })
export class GhostfolioMeetsInternetIdentityPageComponent { export class GhostfolioMeetsInternetIdentityPageComponent {
public routerLinkBlog = ['/' + routes.blog]; public routerLinkBlog = publicRoutes.blog.routerLink;
} }

6
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,4 +1,4 @@
import { routes } from '@ghostfolio/common/routes/routes'; import { publicRoutes } from '@ghostfolio/common/routes/routes';
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
@ -11,6 +11,6 @@ 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 routerLinkBlog = ['/' + routes.blog]; public routerLinkBlog = publicRoutes.blog.routerLink;
public routerLinkResources = ['/' + routes.resources]; public routerLinkResources = publicRoutes.resources.routerLink;
} }

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

@ -1,4 +1,4 @@
import { routes } from '@ghostfolio/common/routes/routes'; import { publicRoutes } from '@ghostfolio/common/routes/routes';
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
@ -11,7 +11,7 @@ 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 routerLinkBlog = ['/' + routes.blog]; public routerLinkBlog = publicRoutes.blog.routerLink;
public routerLinkMarkets = ['/' + routes.markets]; public routerLinkMarkets = publicRoutes.markets.routerLink;
public routerLinkPricing = ['/' + routes.pricing]; public routerLinkPricing = publicRoutes.pricing.routerLink;
} }

4
apps/client/src/app/pages/blog/2022/10/hacktoberfest-2022/hacktoberfest-2022-page.component.ts

@ -1,4 +1,4 @@
import { routes } from '@ghostfolio/common/routes/routes'; import { publicRoutes } from '@ghostfolio/common/routes/routes';
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
@ -11,5 +11,5 @@ import { RouterModule } from '@angular/router';
templateUrl: './hacktoberfest-2022-page.html' templateUrl: './hacktoberfest-2022-page.html'
}) })
export class Hacktoberfest2022PageComponent { export class Hacktoberfest2022PageComponent {
public routerLinkBlog = ['/' + routes.blog]; public routerLinkBlog = publicRoutes.blog.routerLink;
} }

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

@ -1,4 +1,4 @@
import { publicRoutes, routes } from '@ghostfolio/common/routes/routes'; import { publicRoutes } from '@ghostfolio/common/routes/routes';
import { GfPremiumIndicatorComponent } from '@ghostfolio/ui/premium-indicator'; import { GfPremiumIndicatorComponent } from '@ghostfolio/ui/premium-indicator';
import { Component } from '@angular/core'; import { Component } from '@angular/core';
@ -12,7 +12,7 @@ import { RouterModule } from '@angular/router';
templateUrl: './black-friday-2022-page.html' templateUrl: './black-friday-2022-page.html'
}) })
export class BlackFriday2022PageComponent { export class BlackFriday2022PageComponent {
public routerLinkBlog = ['/' + routes.blog]; public routerLinkBlog = publicRoutes.blog.routerLink;
public routerLinkFeatures = publicRoutes.features.routerLink; public routerLinkFeatures = publicRoutes.features.routerLink;
public routerLinkPricing = ['/' + routes.pricing]; public routerLinkPricing = publicRoutes.pricing.routerLink;
} }

4
apps/client/src/app/pages/blog/2022/12/the-importance-of-tracking-your-personal-finances/the-importance-of-tracking-your-personal-finances-page.component.ts

@ -1,4 +1,4 @@
import { routes } from '@ghostfolio/common/routes/routes'; import { publicRoutes } from '@ghostfolio/common/routes/routes';
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
@ -11,5 +11,5 @@ import { RouterModule } from '@angular/router';
templateUrl: './the-importance-of-tracking-your-personal-finances-page.html' templateUrl: './the-importance-of-tracking-your-personal-finances-page.html'
}) })
export class TheImportanceOfTrackingYourPersonalFinancesPageComponent { export class TheImportanceOfTrackingYourPersonalFinancesPageComponent {
public routerLinkBlog = ['/' + routes.blog]; public routerLinkBlog = publicRoutes.blog.routerLink;
} }

4
apps/client/src/app/pages/blog/2023/01/ghostfolio-auf-sackgeld-vorgestellt/ghostfolio-auf-sackgeld-vorgestellt-page.component.ts

@ -1,4 +1,4 @@
import { routes } from '@ghostfolio/common/routes/routes'; import { publicRoutes } from '@ghostfolio/common/routes/routes';
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
@ -11,5 +11,5 @@ import { RouterModule } from '@angular/router';
templateUrl: './ghostfolio-auf-sackgeld-vorgestellt-page.html' templateUrl: './ghostfolio-auf-sackgeld-vorgestellt-page.html'
}) })
export class GhostfolioAufSackgeldVorgestelltPageComponent { export class GhostfolioAufSackgeldVorgestelltPageComponent {
public routerLinkBlog = ['/' + routes.blog]; public routerLinkBlog = publicRoutes.blog.routerLink;
} }

4
apps/client/src/app/pages/blog/2023/02/ghostfolio-meets-umbrel/ghostfolio-meets-umbrel-page.component.ts

@ -1,4 +1,4 @@
import { routes } from '@ghostfolio/common/routes/routes'; import { publicRoutes } from '@ghostfolio/common/routes/routes';
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
@ -11,5 +11,5 @@ import { RouterModule } from '@angular/router';
templateUrl: './ghostfolio-meets-umbrel-page.html' templateUrl: './ghostfolio-meets-umbrel-page.html'
}) })
export class GhostfolioMeetsUmbrelPageComponent { export class GhostfolioMeetsUmbrelPageComponent {
public routerLinkBlog = ['/' + routes.blog]; public routerLinkBlog = publicRoutes.blog.routerLink;
} }

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

@ -1,4 +1,4 @@
import { publicRoutes, routes } from '@ghostfolio/common/routes/routes'; import { publicRoutes } from '@ghostfolio/common/routes/routes';
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
@ -11,7 +11,7 @@ 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 routerLinkBlog = ['/' + routes.blog]; public routerLinkBlog = publicRoutes.blog.routerLink;
public routerLinkFeatures = publicRoutes.features.routerLink; public routerLinkFeatures = publicRoutes.features.routerLink;
public routerLinkPricing = ['/' + routes.pricing]; public routerLinkPricing = publicRoutes.pricing.routerLink;
} }

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,4 +1,4 @@
import { publicRoutes, routes } from '@ghostfolio/common/routes/routes'; import { publicRoutes } from '@ghostfolio/common/routes/routes';
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
@ -11,7 +11,7 @@ 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 routerLinkBlog = ['/' + routes.blog]; public routerLinkBlog = publicRoutes.blog.routerLink;
public routerLinkFeatures = publicRoutes.features.routerLink; public routerLinkFeatures = publicRoutes.features.routerLink;
public routerLinkResources = ['/' + routes.resources]; public routerLinkResources = publicRoutes.resources.routerLink;
} }

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

@ -1,4 +1,4 @@
import { publicRoutes, routes } from '@ghostfolio/common/routes/routes'; import { publicRoutes } from '@ghostfolio/common/routes/routes';
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
@ -11,6 +11,6 @@ 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 routerLinkBlog = ['/' + routes.blog]; public routerLinkBlog = publicRoutes.blog.routerLink;
public routerLinkFeatures = publicRoutes.features.routerLink; public routerLinkFeatures = publicRoutes.features.routerLink;
} }

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

@ -1,4 +1,4 @@
import { routes } from '@ghostfolio/common/routes/routes'; import { publicRoutes } from '@ghostfolio/common/routes/routes';
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
@ -11,6 +11,7 @@ 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 = ['/' + routes.about, routes.ossFriends]; public routerLinkAboutOssFriends =
public routerLinkBlog = ['/' + routes.blog]; publicRoutes.about.subRoutes.ossFriends.routerLink;
public routerLinkBlog = publicRoutes.blog.routerLink;
} }

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

@ -1,4 +1,4 @@
import { publicRoutes, routes } from '@ghostfolio/common/routes/routes'; import { publicRoutes } from '@ghostfolio/common/routes/routes';
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
@ -11,9 +11,10 @@ import { RouterModule } from '@angular/router';
templateUrl: './ghostfolio-2-page.html' templateUrl: './ghostfolio-2-page.html'
}) })
export class Ghostfolio2PageComponent { export class Ghostfolio2PageComponent {
public routerLinkAbout = ['/' + routes.about]; public routerLinkAbout = publicRoutes.about.routerLink;
public routerLinkAboutChangelog = ['/' + routes.about, routes.changelog]; public routerLinkAboutChangelog =
public routerLinkBlog = ['/' + routes.blog]; publicRoutes.about.subRoutes.changelog.routerLink;
public routerLinkBlog = publicRoutes.blog.routerLink;
public routerLinkFeatures = publicRoutes.features.routerLink; public routerLinkFeatures = publicRoutes.features.routerLink;
public routerLinkMarkets = ['/' + routes.markets]; public routerLinkMarkets = publicRoutes.markets.routerLink;
} }

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

@ -1,4 +1,4 @@
import { publicRoutes, routes } from '@ghostfolio/common/routes/routes'; import { publicRoutes } from '@ghostfolio/common/routes/routes';
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
@ -11,7 +11,7 @@ import { RouterModule } from '@angular/router';
templateUrl: './hacktoberfest-2023-page.html' templateUrl: './hacktoberfest-2023-page.html'
}) })
export class Hacktoberfest2023PageComponent { export class Hacktoberfest2023PageComponent {
public routerLinkAbout = ['/' + routes.about]; public routerLinkAbout = publicRoutes.about.routerLink;
public routerLinkBlog = ['/' + routes.blog]; public routerLinkBlog = publicRoutes.blog.routerLink;
public routerLinkOpenStartup = publicRoutes.openStartup.routerLink; public routerLinkOpenStartup = publicRoutes.openStartup.routerLink;
} }

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

@ -1,4 +1,4 @@
import { publicRoutes, routes } from '@ghostfolio/common/routes/routes'; import { publicRoutes } from '@ghostfolio/common/routes/routes';
import { GfPremiumIndicatorComponent } from '@ghostfolio/ui/premium-indicator'; import { GfPremiumIndicatorComponent } from '@ghostfolio/ui/premium-indicator';
import { Component } from '@angular/core'; import { Component } from '@angular/core';
@ -12,7 +12,7 @@ import { RouterModule } from '@angular/router';
templateUrl: './black-week-2023-page.html' templateUrl: './black-week-2023-page.html'
}) })
export class BlackWeek2023PageComponent { export class BlackWeek2023PageComponent {
public routerLinkBlog = ['/' + routes.blog]; public routerLinkBlog = publicRoutes.blog.routerLink;
public routerLinkFeatures = publicRoutes.features.routerLink; public routerLinkFeatures = publicRoutes.features.routerLink;
public routerLinkPricing = ['/' + routes.pricing]; public routerLinkPricing = publicRoutes.pricing.routerLink;
} }

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

@ -1,4 +1,4 @@
import { publicRoutes, routes } from '@ghostfolio/common/routes/routes'; import { publicRoutes } from '@ghostfolio/common/routes/routes';
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
@ -11,7 +11,7 @@ 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 = ['/' + routes.about]; public routerLinkAbout = publicRoutes.about.routerLink;
public routerLinkBlog = ['/' + routes.blog]; public routerLinkBlog = publicRoutes.blog.routerLink;
public routerLinkFeatures = publicRoutes.features.routerLink; public routerLinkFeatures = publicRoutes.features.routerLink;
} }

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

@ -1,4 +1,4 @@
import { publicRoutes, routes } from '@ghostfolio/common/routes/routes'; import { publicRoutes } from '@ghostfolio/common/routes/routes';
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
@ -11,7 +11,7 @@ import { RouterModule } from '@angular/router';
templateUrl: './hacktoberfest-2024-page.html' templateUrl: './hacktoberfest-2024-page.html'
}) })
export class Hacktoberfest2024PageComponent { export class Hacktoberfest2024PageComponent {
public routerLinkAbout = ['/' + routes.about]; public routerLinkAbout = publicRoutes.about.routerLink;
public routerLinkBlog = ['/' + routes.blog]; public routerLinkBlog = publicRoutes.blog.routerLink;
public routerLinkOpenStartup = publicRoutes.openStartup.routerLink; public routerLinkOpenStartup = publicRoutes.openStartup.routerLink;
} }

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

@ -1,4 +1,4 @@
import { publicRoutes, routes } from '@ghostfolio/common/routes/routes'; import { publicRoutes } from '@ghostfolio/common/routes/routes';
import { GfPremiumIndicatorComponent } from '@ghostfolio/ui/premium-indicator'; import { GfPremiumIndicatorComponent } from '@ghostfolio/ui/premium-indicator';
import { Component } from '@angular/core'; import { Component } from '@angular/core';
@ -12,7 +12,7 @@ import { RouterModule } from '@angular/router';
templateUrl: './black-weeks-2024-page.html' templateUrl: './black-weeks-2024-page.html'
}) })
export class BlackWeeks2024PageComponent { export class BlackWeeks2024PageComponent {
public routerLinkBlog = ['/' + routes.blog]; public routerLinkBlog = publicRoutes.blog.routerLink;
public routerLinkFeatures = publicRoutes.features.routerLink; public routerLinkFeatures = publicRoutes.features.routerLink;
public routerLinkPricing = ['/' + routes.pricing]; public routerLinkPricing = publicRoutes.pricing.routerLink;
} }

3
apps/client/src/app/pages/blog/blog-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 { publicRoutes } from '@ghostfolio/common/routes/routes';
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router';
@ -10,7 +11,7 @@ const routes: Routes = [
canActivate: [AuthGuard], canActivate: [AuthGuard],
component: BlogPageComponent, component: BlogPageComponent,
path: '', path: '',
title: $localize`Blog` title: publicRoutes.blog.title
}, },
{ {
canActivate: [AuthGuard], canActivate: [AuthGuard],

8
apps/client/src/app/pages/faq/faq-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 { routes as ghostfolioRoutes } from '@ghostfolio/common/routes/routes'; import { publicRoutes } from '@ghostfolio/common/routes/routes';
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router';
@ -18,12 +18,12 @@ const routes: Routes = [
) )
}, },
{ {
path: ghostfolioRoutes.saas, path: publicRoutes.faq.subRoutes.saas.path,
loadChildren: () => loadChildren: () =>
import('./saas/saas-page.module').then((m) => m.SaasPageModule) import('./saas/saas-page.module').then((m) => m.SaasPageModule)
}, },
{ {
path: ghostfolioRoutes.selfHosting, path: publicRoutes.faq.subRoutes.selfHosting.path,
loadChildren: () => loadChildren: () =>
import('./self-hosting/self-hosting-page.module').then( import('./self-hosting/self-hosting-page.module').then(
(m) => m.SelfHostingPageModule (m) => m.SelfHostingPageModule
@ -32,7 +32,7 @@ const routes: Routes = [
], ],
component: FaqPageComponent, component: FaqPageComponent,
path: '', path: '',
title: $localize`Frequently Asked Questions (FAQ)` title: publicRoutes.faq.title
} }
]; ];

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

@ -1,7 +1,7 @@
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 { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import { routes } from '@ghostfolio/common/routes/routes'; import { publicRoutes } from '@ghostfolio/common/routes/routes';
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';
@ -36,18 +36,18 @@ export class FaqPageComponent implements OnDestroy, OnInit {
{ {
iconName: 'reader-outline', iconName: 'reader-outline',
label: $localize`General`, label: $localize`General`,
path: ['/' + routes.faq] routerLink: publicRoutes.faq.routerLink
}, },
{ {
iconName: 'cloudy-outline', iconName: 'cloudy-outline',
label: $localize`Cloud` + ' (SaaS)', label: $localize`Cloud` + ' (SaaS)',
path: ['/' + routes.faq, routes.saas], routerLink: publicRoutes.faq.subRoutes.saas.routerLink,
showCondition: this.hasPermissionForSubscription showCondition: this.hasPermissionForSubscription
}, },
{ {
iconName: 'server-outline', iconName: 'server-outline',
label: $localize`Self-Hosting`, label: $localize`Self-Hosting`,
path: ['/' + routes.faq, routes.selfHosting] routerLink: publicRoutes.faq.subRoutes.selfHosting.routerLink
} }
]; ];
} }

2
apps/client/src/app/pages/faq/faq-page.html

@ -16,7 +16,7 @@
mat-tab-link mat-tab-link
routerLinkActive routerLinkActive
[active]="rla.isActive" [active]="rla.isActive"
[routerLink]="tab.path" [routerLink]="tab.routerLink"
[routerLinkActiveOptions]="{ exact: true }" [routerLinkActiveOptions]="{ exact: true }"
> >
<ion-icon <ion-icon

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

@ -1,6 +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 { publicRoutes, routes } from '@ghostfolio/common/routes/routes'; import { publicRoutes } from '@ghostfolio/common/routes/routes';
import { ChangeDetectorRef, Component, OnDestroy } from '@angular/core'; import { ChangeDetectorRef, Component, OnDestroy } from '@angular/core';
import { Subject, takeUntil } from 'rxjs'; import { Subject, takeUntil } from 'rxjs';
@ -13,7 +13,7 @@ import { Subject, takeUntil } from 'rxjs';
standalone: false standalone: false
}) })
export class FaqOverviewPageComponent implements OnDestroy { export class FaqOverviewPageComponent implements OnDestroy {
public pricingUrl = `https://ghostfol.io/${document.documentElement.lang}/${routes.pricing}`; public pricingUrl = `https://ghostfol.io/${document.documentElement.lang}/${publicRoutes.pricing.path}`;
public routerLinkFeatures = publicRoutes.features.routerLink; public routerLinkFeatures = publicRoutes.features.routerLink;
public user: User; public user: User;

3
apps/client/src/app/pages/faq/saas/saas-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 { publicRoutes } from '@ghostfolio/common/routes/routes';
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router';
@ -10,7 +11,7 @@ const routes: Routes = [
canActivate: [AuthGuard], canActivate: [AuthGuard],
component: SaasPageComponent, component: SaasPageComponent,
path: '', path: '',
title: $localize`Cloud` + ' (SaaS) – ' + $localize`FAQ` title: `${publicRoutes.faq.subRoutes.saas.title} - ${publicRoutes.faq.title}`
} }
]; ];

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

@ -1,10 +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 { import { internalRoutes, publicRoutes } from '@ghostfolio/common/routes/routes';
internalRoutes,
publicRoutes,
routes
} from '@ghostfolio/common/routes/routes';
import { ChangeDetectorRef, Component, OnDestroy } from '@angular/core'; import { ChangeDetectorRef, Component, OnDestroy } from '@angular/core';
import { Subject, takeUntil } from 'rxjs'; import { Subject, takeUntil } from 'rxjs';
@ -17,13 +13,11 @@ import { Subject, takeUntil } from 'rxjs';
standalone: false standalone: false
}) })
export class SaasPageComponent implements OnDestroy { export class SaasPageComponent implements OnDestroy {
public pricingUrl = `https://ghostfol.io/${document.documentElement.lang}/${routes.pricing}`; public pricingUrl = `https://ghostfol.io/${document.documentElement.lang}/${publicRoutes.pricing.path}`;
public routerLinkAccount = internalRoutes.account.routerLink; public routerLinkAccount = internalRoutes.account.routerLink;
public routerLinkAccountMembership = [ public routerLinkAccountMembership =
'/' + internalRoutes.account.path, internalRoutes.account.subRoutes.membership.routerLink;
routes.membership public routerLinkMarkets = publicRoutes.markets.routerLink;
];
public routerLinkMarkets = ['/' + routes.markets];
public routerLinkRegister = publicRoutes.register.routerLink; public routerLinkRegister = publicRoutes.register.routerLink;
public user: User; public user: User;

3
apps/client/src/app/pages/faq/self-hosting/self-hosting-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 { publicRoutes } from '@ghostfolio/common/routes/routes';
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router';
@ -10,7 +11,7 @@ const routes: Routes = [
canActivate: [AuthGuard], canActivate: [AuthGuard],
component: SelfHostingPageComponent, component: SelfHostingPageComponent,
path: '', path: '',
title: $localize`Self-Hosting` + ' – ' + $localize`FAQ` title: `${publicRoutes.faq.subRoutes.selfHosting.title} - ${publicRoutes.faq.title}`
} }
]; ];

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

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

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

@ -2,7 +2,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 { InfoItem, User } from '@ghostfolio/common/interfaces'; import { InfoItem, User } from '@ghostfolio/common/interfaces';
import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import { publicRoutes, routes } from '@ghostfolio/common/routes/routes'; import { publicRoutes } from '@ghostfolio/common/routes/routes';
import { GfPremiumIndicatorComponent } from '@ghostfolio/ui/premium-indicator'; import { GfPremiumIndicatorComponent } from '@ghostfolio/ui/premium-indicator';
import { ChangeDetectorRef, Component, OnDestroy } from '@angular/core'; import { ChangeDetectorRef, Component, OnDestroy } from '@angular/core';
@ -27,7 +27,7 @@ export class GfFeaturesPageComponent implements OnDestroy {
public hasPermissionForSubscription: boolean; public hasPermissionForSubscription: boolean;
public info: InfoItem; public info: InfoItem;
public routerLinkRegister = publicRoutes.register.routerLink; public routerLinkRegister = publicRoutes.register.routerLink;
public routerLinkResources = ['/' + routes.resources]; public routerLinkResources = publicRoutes.resources.routerLink;
public user: User; public user: User;
private unsubscribeSubject = new Subject<void>(); private unsubscribeSubject = new Subject<void>();

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

@ -4,10 +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 { import { internalRoutes } from '@ghostfolio/common/routes/routes';
routes as ghostfolioRoutes,
internalRoutes
} from '@ghostfolio/common/routes/routes';
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router';
@ -33,9 +30,9 @@ const routes: Routes = [
title: internalRoutes.home.subRoutes.summary.title title: internalRoutes.home.subRoutes.summary.title
}, },
{ {
path: ghostfolioRoutes.market, path: internalRoutes.home.subRoutes.markets.path,
component: HomeMarketComponent, component: HomeMarketComponent,
title: $localize`Markets` title: internalRoutes.home.subRoutes.markets.title
}, },
{ {
path: internalRoutes.home.subRoutes.watchlist.path, path: internalRoutes.home.subRoutes.watchlist.path,

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

@ -1,7 +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 { internalRoutes, routes } from '@ghostfolio/common/routes/routes'; import { internalRoutes } from '@ghostfolio/common/routes/routes';
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';
@ -37,27 +37,27 @@ export class HomePageComponent implements OnDestroy, OnInit {
{ {
iconName: 'analytics-outline', iconName: 'analytics-outline',
label: internalRoutes.home.title, label: internalRoutes.home.title,
path: internalRoutes.home.routerLink routerLink: internalRoutes.home.routerLink
}, },
{ {
iconName: 'wallet-outline', iconName: 'wallet-outline',
label: internalRoutes.home.subRoutes.holdings.title, label: internalRoutes.home.subRoutes.holdings.title,
path: internalRoutes.home.subRoutes.holdings.routerLink routerLink: internalRoutes.home.subRoutes.holdings.routerLink
}, },
{ {
iconName: 'reader-outline', iconName: 'reader-outline',
label: internalRoutes.home.subRoutes.summary.title, label: internalRoutes.home.subRoutes.summary.title,
path: internalRoutes.home.subRoutes.summary.routerLink routerLink: internalRoutes.home.subRoutes.summary.routerLink
}, },
{ {
iconName: 'bookmark-outline', iconName: 'bookmark-outline',
label: internalRoutes.home.subRoutes.watchlist.title, label: internalRoutes.home.subRoutes.watchlist.title,
path: internalRoutes.home.subRoutes.watchlist.routerLink routerLink: internalRoutes.home.subRoutes.watchlist.routerLink
}, },
{ {
iconName: 'newspaper-outline', iconName: 'newspaper-outline',
label: $localize`Markets`, label: internalRoutes.home.subRoutes.markets.title,
path: ['/' + internalRoutes.home.path, routes.market] routerLink: internalRoutes.home.subRoutes.markets.routerLink
} }
]; ];

2
apps/client/src/app/pages/home/home-page.html

@ -16,7 +16,7 @@
mat-tab-link mat-tab-link
routerLinkActive routerLinkActive
[active]="rla.isActive" [active]="rla.isActive"
[routerLink]="tab.path" [routerLink]="tab.routerLink"
[routerLinkActiveOptions]="{ exact: true }" [routerLinkActiveOptions]="{ exact: true }"
> >
<ion-icon <ion-icon

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

@ -1,7 +1,7 @@
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 { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import { publicRoutes, routes } from '@ghostfolio/common/routes/routes'; import { publicRoutes } from '@ghostfolio/common/routes/routes';
import { Component, OnDestroy, OnInit } from '@angular/core'; import { Component, OnDestroy, OnInit } from '@angular/core';
import { format } from 'date-fns'; import { format } from 'date-fns';
@ -25,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 = ['/' + routes.about]; public routerLinkAbout = publicRoutes.about.routerLink;
public routerLinkDemo = ['/' + routes.demo]; public routerLinkDemo = publicRoutes.demo.routerLink;
public routerLinkOpenStartup = publicRoutes.openStartup.routerLink; public routerLinkOpenStartup = publicRoutes.openStartup.routerLink;
public routerLinkRegister = publicRoutes.register.routerLink; public routerLinkRegister = publicRoutes.register.routerLink;
public statistics: Statistics; public statistics: Statistics;

3
apps/client/src/app/pages/markets/markets-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 { publicRoutes } from '@ghostfolio/common/routes/routes';
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router';
@ -10,7 +11,7 @@ const routes: Routes = [
canActivate: [AuthGuard], canActivate: [AuthGuard],
component: MarketsPageComponent, component: MarketsPageComponent,
path: '', path: '',
title: $localize`Markets` title: publicRoutes.markets.title
} }
]; ];

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

@ -34,27 +34,29 @@ export class PortfolioPageComponent implements OnDestroy, OnInit {
{ {
iconName: 'analytics-outline', iconName: 'analytics-outline',
label: internalRoutes.portfolio.subRoutes.analysis.title, label: internalRoutes.portfolio.subRoutes.analysis.title,
path: internalRoutes.portfolio.routerLink routerLink: internalRoutes.portfolio.routerLink
}, },
{ {
iconName: 'swap-vertical-outline', iconName: 'swap-vertical-outline',
label: internalRoutes.portfolio.subRoutes.activities.title, label: internalRoutes.portfolio.subRoutes.activities.title,
path: internalRoutes.portfolio.subRoutes.activities.routerLink routerLink:
internalRoutes.portfolio.subRoutes.activities.routerLink
}, },
{ {
iconName: 'pie-chart-outline', iconName: 'pie-chart-outline',
label: internalRoutes.portfolio.subRoutes.allocations.title, label: internalRoutes.portfolio.subRoutes.allocations.title,
path: internalRoutes.portfolio.subRoutes.allocations.routerLink routerLink:
internalRoutes.portfolio.subRoutes.allocations.routerLink
}, },
{ {
iconName: 'calculator-outline', iconName: 'calculator-outline',
label: internalRoutes.portfolio.subRoutes.fire.title, label: internalRoutes.portfolio.subRoutes.fire.title,
path: internalRoutes.portfolio.subRoutes.fire.routerLink routerLink: internalRoutes.portfolio.subRoutes.fire.routerLink
}, },
{ {
iconName: 'scan-outline', iconName: 'scan-outline',
label: internalRoutes.portfolio.subRoutes.xRay.title, label: internalRoutes.portfolio.subRoutes.xRay.title,
path: internalRoutes.portfolio.subRoutes.xRay.routerLink routerLink: internalRoutes.portfolio.subRoutes.xRay.routerLink
} }
]; ];
this.user = state.user; this.user = state.user;

2
apps/client/src/app/pages/portfolio/portfolio-page.html

@ -16,7 +16,7 @@
mat-tab-link mat-tab-link
routerLinkActive routerLinkActive
[active]="rla.isActive" [active]="rla.isActive"
[routerLink]="tab.path" [routerLink]="tab.routerLink"
[routerLinkActiveOptions]="{ exact: true }" [routerLinkActiveOptions]="{ exact: true }"
> >
<ion-icon <ion-icon

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

@ -1,5 +1,5 @@
import { DataService } from '@ghostfolio/client/services/data.service'; import { DataService } from '@ghostfolio/client/services/data.service';
import { routes } from '@ghostfolio/common/routes/routes'; import { publicRoutes } from '@ghostfolio/common/routes/routes';
import { import {
ChangeDetectionStrategy, ChangeDetectionStrategy,
@ -30,10 +30,8 @@ export class ShowAccessTokenDialog {
public isCreateAccountButtonDisabled = true; public isCreateAccountButtonDisabled = true;
public isDisclaimerChecked = false; public isDisclaimerChecked = false;
public role: string; public role: string;
public routerLinkAboutTermsOfService = [ public routerLinkAboutTermsOfService =
'/' + routes.about, publicRoutes.about.subRoutes.termsOfService.routerLink;
routes.termsOfService
];
private unsubscribeSubject = new Subject<void>(); private unsubscribeSubject = new Subject<void>();

4
apps/client/src/app/pages/resources/glossary/resources-glossary-routing.module.ts

@ -1,3 +1,5 @@
import { publicRoutes } from '@ghostfolio/common/routes/routes';
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router';
@ -7,7 +9,7 @@ const routes: Routes = [
{ {
component: ResourcesGlossaryPageComponent, component: ResourcesGlossaryPageComponent,
path: '', path: '',
title: $localize`Glossary` title: publicRoutes.resources.subRoutes.glossary.title
} }
]; ];

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

@ -1,7 +1,7 @@
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 { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import { routes } from '@ghostfolio/common/routes/routes'; import { publicRoutes } from '@ghostfolio/common/routes/routes';
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
@ -14,10 +14,8 @@ import { Component, OnInit } from '@angular/core';
export class ResourcesGlossaryPageComponent implements OnInit { export class ResourcesGlossaryPageComponent implements OnInit {
public hasPermissionForSubscription: boolean; public hasPermissionForSubscription: boolean;
public info: InfoItem; public info: InfoItem;
public routerLinkResourcesPersonalFinanceTools = [ public routerLinkResourcesPersonalFinanceTools =
'/' + routes.resources, publicRoutes.resources.subRoutes.personalFinanceTools.routerLink;
routes.personalFinanceTools
];
public constructor(private dataService: DataService) { public constructor(private dataService: DataService) {
this.info = this.dataService.fetchInfo(); this.info = this.dataService.fetchInfo();

4
apps/client/src/app/pages/resources/guides/resources-guides-routing.module.ts

@ -1,3 +1,5 @@
import { publicRoutes } from '@ghostfolio/common/routes/routes';
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router';
@ -7,7 +9,7 @@ const routes: Routes = [
{ {
component: ResourcesGuidesComponent, component: ResourcesGuidesComponent,
path: '', path: '',
title: $localize`Guides` title: publicRoutes.resources.subRoutes.guides.title
} }
]; ];

4
apps/client/src/app/pages/resources/markets/resources-markets-routing.module.ts

@ -1,3 +1,5 @@
import { publicRoutes } from '@ghostfolio/common/routes/routes';
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router';
@ -7,7 +9,7 @@ const routes: Routes = [
{ {
component: ResourcesMarketsComponent, component: ResourcesMarketsComponent,
path: '', path: '',
title: $localize`Markets` title: publicRoutes.resources.subRoutes.markets.title
} }
]; ];

2
apps/client/src/app/pages/resources/overview/resources-overview.component.html

@ -7,7 +7,7 @@
<div class="mb-4"> <div class="mb-4">
<h3 class="h5 mt-0">{{ item.title }}</h3> <h3 class="h5 mt-0">{{ item.title }}</h3>
<p class="mb-1">{{ item.description }}</p> <p class="mb-1">{{ item.description }}</p>
<a [routerLink]="item.link">Explore {{ item.title }} →</a> <a [routerLink]="item.routerLink">Explore {{ item.title }} →</a>
</div> </div>
} }
</div> </div>

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

@ -1,4 +1,4 @@
import { routes } from '@ghostfolio/common/routes/routes'; import { publicRoutes } from '@ghostfolio/common/routes/routes';
import { Component } from '@angular/core'; import { Component } from '@angular/core';
@ -11,28 +11,28 @@ import { Component } from '@angular/core';
export class ResourcesOverviewComponent { export class ResourcesOverviewComponent {
public overviewItems = [ public overviewItems = [
{ {
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: ['/' + routes.faq] routerLink: publicRoutes.faq.routerLink,
title: publicRoutes.faq.title
}, },
{ {
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: ['/' + routes.resources, routes.guides] routerLink: publicRoutes.resources.subRoutes.guides.routerLink,
title: publicRoutes.resources.subRoutes.guides.title
}, },
{ {
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: ['/' + routes.resources, routes.markets] routerLink: publicRoutes.resources.subRoutes.markets.routerLink,
title: publicRoutes.resources.subRoutes.markets.title
}, },
{ {
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: ['/' + routes.resources, routes.glossary] routerLink: publicRoutes.resources.subRoutes.glossary.routerLink,
title: publicRoutes.resources.subRoutes.glossary.title
} }
]; ];
} }

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

@ -1,6 +1,6 @@
import { AuthGuard } from '@ghostfolio/client/core/auth.guard'; import { AuthGuard } from '@ghostfolio/client/core/auth.guard';
import { personalFinanceTools } from '@ghostfolio/common/personal-finance-tools'; import { personalFinanceTools } from '@ghostfolio/common/personal-finance-tools';
import { routes as ghostfolioRoutes } from '@ghostfolio/common/routes/routes'; import { publicRoutes } from '@ghostfolio/common/routes/routes';
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router';
@ -12,7 +12,7 @@ const routes: Routes = [
canActivate: [AuthGuard], canActivate: [AuthGuard],
component: PersonalFinanceToolsPageComponent, component: PersonalFinanceToolsPageComponent,
path: '', path: '',
title: $localize`Personal Finance Tools` title: publicRoutes.resources.subRoutes.personalFinanceTools.title
}, },
...personalFinanceTools.map(({ alias, key, name }) => { ...personalFinanceTools.map(({ alias, key, name }) => {
return { return {
@ -24,8 +24,8 @@ const routes: Routes = [
return GfProductPageComponent; return GfProductPageComponent;
} }
), ),
path: `${ghostfolioRoutes.openSourceAlternativeTo}-${alias ?? key}`, path: `${publicRoutes.resources.subRoutes.personalFinanceTools.subRoutes.product.path}-${alias ?? key}`,
title: $localize`Open Source Alternative to ${name}` title: `${publicRoutes.resources.subRoutes.personalFinanceTools.subRoutes.product.title} ${name}`
}; };
}) })
]; ];

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

@ -1,5 +1,5 @@
import { personalFinanceTools } from '@ghostfolio/common/personal-finance-tools'; import { personalFinanceTools } from '@ghostfolio/common/personal-finance-tools';
import { routes } from '@ghostfolio/common/routes/routes'; import { publicRoutes } from '@ghostfolio/common/routes/routes';
import { Component, OnDestroy } from '@angular/core'; import { Component, OnDestroy } from '@angular/core';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
@ -12,12 +12,16 @@ import { Subject } from 'rxjs';
standalone: false standalone: false
}) })
export class PersonalFinanceToolsPageComponent implements OnDestroy { export class PersonalFinanceToolsPageComponent implements OnDestroy {
public pathAlternativeTo = routes.openSourceAlternativeTo + '-'; public pathAlternativeTo =
public pathResources = '/' + routes.resources; publicRoutes.resources.subRoutes.personalFinanceTools.subRoutes.product
.path + '-';
public pathResources = publicRoutes.resources.path;
public pathPersonalFinanceTools =
publicRoutes.resources.subRoutes.personalFinanceTools.path;
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 = ['/' + routes.about]; public routerLinkAbout = publicRoutes.about.routerLink;
private unsubscribeSubject = new Subject<void>(); private unsubscribeSubject = new Subject<void>();

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

@ -32,8 +32,8 @@
personalFinanceTool.name personalFinanceTool.name
}} - {{ personalFinanceTool.slogan }}" }} - {{ personalFinanceTool.slogan }}"
[routerLink]="[ [routerLink]="[
pathResources, '/' + pathResources,
'personal-finance-tools', pathPersonalFinanceTools,
pathAlternativeTo + pathAlternativeTo +
(personalFinanceTool.alias ?? personalFinanceTool.key) (personalFinanceTool.alias ?? personalFinanceTool.key)
]" ]"

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

@ -1,7 +1,7 @@
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 { personalFinanceTools } from '@ghostfolio/common/personal-finance-tools'; import { personalFinanceTools } from '@ghostfolio/common/personal-finance-tools';
import { publicRoutes, routes } from '@ghostfolio/common/routes/routes'; import { publicRoutes } from '@ghostfolio/common/routes/routes';
import { translate } from '@ghostfolio/ui/i18n'; import { translate } from '@ghostfolio/ui/i18n';
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
@ -20,12 +20,10 @@ 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 = ['/' + routes.about]; public routerLinkAbout = publicRoutes.about.routerLink;
public routerLinkFeatures = publicRoutes.features.routerLink; public routerLinkFeatures = publicRoutes.features.routerLink;
public routerLinkResourcesPersonalFinanceTools = [ public routerLinkResourcesPersonalFinanceTools =
'/' + routes.resources, publicRoutes.resources.subRoutes.personalFinanceTools.routerLink;
routes.personalFinanceTools
];
public tags: string[]; public tags: string[];
public constructor( public constructor(

16
apps/client/src/app/pages/resources/resources-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 { routes as ghostfolioRoutes } from '@ghostfolio/common/routes/routes'; import { publicRoutes } from '@ghostfolio/common/routes/routes';
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router';
@ -19,36 +19,36 @@ const routes: Routes = [
) )
}, },
{ {
path: ghostfolioRoutes.glossary, path: publicRoutes.resources.subRoutes.glossary.path,
loadChildren: () => loadChildren: () =>
import('./glossary/resources-glossary.module').then( import('./glossary/resources-glossary.module').then(
(m) => m.ResourcesGlossaryPageModule (m) => m.ResourcesGlossaryPageModule
) )
}, },
{ {
path: ghostfolioRoutes.guides, path: publicRoutes.resources.subRoutes.guides.path,
loadChildren: () => loadChildren: () =>
import('./guides/resources-guides.module').then( import('./guides/resources-guides.module').then(
(m) => m.ResourcesGuidesModule (m) => m.ResourcesGuidesModule
) )
}, },
{ {
path: ghostfolioRoutes.markets, path: publicRoutes.resources.subRoutes.markets.path,
loadChildren: () => loadChildren: () =>
import('./markets/resources-markets.module').then( import('./markets/resources-markets.module').then(
(m) => m.ResourcesMarketsModule (m) => m.ResourcesMarketsModule
) )
}, },
...[ghostfolioRoutes.personalFinanceTools].map((path) => ({ {
path, path: publicRoutes.resources.subRoutes.personalFinanceTools.path,
loadChildren: () => loadChildren: () =>
import( import(
'./personal-finance-tools/personal-finance-tools-page.module' './personal-finance-tools/personal-finance-tools-page.module'
).then((m) => m.PersonalFinanceToolsPageModule) ).then((m) => m.PersonalFinanceToolsPageModule)
})) }
], ],
path: '', path: '',
title: $localize`Resources` title: publicRoutes.resources.title
} }
]; ];

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

@ -1,4 +1,5 @@
import { routes } from '@ghostfolio/common/routes/routes'; import { TabConfiguration } from '@ghostfolio/common/interfaces';
import { publicRoutes } from '@ghostfolio/common/routes/routes';
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { DeviceDetectorService } from 'ngx-device-detector'; import { DeviceDetectorService } from 'ngx-device-detector';
@ -13,26 +14,26 @@ import { Subject } from 'rxjs';
}) })
export class ResourcesPageComponent implements OnInit { export class ResourcesPageComponent implements OnInit {
public deviceType: string; public deviceType: string;
public tabs = [ public tabs: TabConfiguration[] = [
{ {
path: '.', iconName: 'reader-outline',
label: $localize`Overview`, label: $localize`Overview`,
iconName: 'reader-outline' routerLink: publicRoutes.resources.routerLink
}, },
{ {
path: routes.guides,
label: $localize`Guides`, label: $localize`Guides`,
iconName: 'book-outline' iconName: 'book-outline',
routerLink: publicRoutes.resources.subRoutes.guides.routerLink
}, },
{ {
path: routes.markets, iconName: 'newspaper-outline',
label: $localize`Markets`, label: $localize`Markets`,
iconName: 'newspaper-outline' routerLink: publicRoutes.resources.subRoutes.markets.routerLink
}, },
{ {
path: routes.glossary, iconName: 'library-outline',
label: $localize`Glossary`, label: $localize`Glossary`,
iconName: 'library-outline' routerLink: publicRoutes.resources.subRoutes.glossary.routerLink
} }
]; ];

2
apps/client/src/app/pages/resources/resources-page.html

@ -16,7 +16,7 @@
mat-tab-link mat-tab-link
routerLinkActive routerLinkActive
[active]="rla.isActive" [active]="rla.isActive"
[routerLink]="tab.path" [routerLink]="tab.routerLink"
[routerLinkActiveOptions]="{ exact: true }" [routerLinkActiveOptions]="{ exact: true }"
> >
<ion-icon <ion-icon

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

@ -2,7 +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 { routes as ghostfolioRoutes } from '@ghostfolio/common/routes/routes'; import { internalRoutes } from '@ghostfolio/common/routes/routes';
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router';
@ -16,17 +16,17 @@ const routes: Routes = [
{ {
path: '', path: '',
component: UserAccountSettingsComponent, component: UserAccountSettingsComponent,
title: $localize`Settings` title: internalRoutes.userAccount.title
}, },
{ {
path: ghostfolioRoutes.membership, path: internalRoutes.userAccount.subRoutes.membership.path,
component: UserAccountMembershipComponent, component: UserAccountMembershipComponent,
title: $localize`Membership` title: internalRoutes.userAccount.subRoutes.membership.title
}, },
{ {
path: ghostfolioRoutes.access, path: internalRoutes.userAccount.subRoutes.access.path,
component: UserAccountAccessComponent, component: UserAccountAccessComponent,
title: $localize`Access` title: internalRoutes.userAccount.subRoutes.access.title
} }
], ],
component: UserAccountPageComponent, component: UserAccountPageComponent,

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

@ -1,6 +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 { internalRoutes, routes } from '@ghostfolio/common/routes/routes'; import { internalRoutes } from '@ghostfolio/common/routes/routes';
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';
@ -35,18 +35,19 @@ export class UserAccountPageComponent implements OnDestroy, OnInit {
{ {
iconName: 'settings-outline', iconName: 'settings-outline',
label: internalRoutes.account.title, label: internalRoutes.account.title,
path: internalRoutes.account.routerLink routerLink: internalRoutes.account.routerLink
}, },
{ {
iconName: 'diamond-outline', iconName: 'diamond-outline',
label: $localize`Membership`, label: internalRoutes.account.subRoutes.membership.title,
path: ['/' + internalRoutes.account.path, routes.membership], routerLink:
internalRoutes.account.subRoutes.membership.routerLink,
showCondition: !!this.user?.subscription showCondition: !!this.user?.subscription
}, },
{ {
iconName: 'key-outline', iconName: 'key-outline',
label: $localize`Access`, label: internalRoutes.account.subRoutes.access.title,
path: ['/' + internalRoutes.account.path, routes.access] routerLink: internalRoutes.account.subRoutes.access.routerLink
} }
]; ];

2
apps/client/src/app/pages/user-account/user-account-page.html

@ -16,7 +16,7 @@
mat-tab-link mat-tab-link
routerLinkActive routerLinkActive
[active]="rla.isActive" [active]="rla.isActive"
[routerLink]="tab.path" [routerLink]="tab.routerLink"
[routerLinkActiveOptions]="{ exact: true }" [routerLinkActiveOptions]="{ exact: true }"
> >
<ion-icon <ion-icon

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

@ -34,12 +34,12 @@ export class ZenPageComponent implements OnDestroy, OnInit {
{ {
iconName: 'analytics-outline', iconName: 'analytics-outline',
label: internalRoutes.zen.title, label: internalRoutes.zen.title,
path: internalRoutes.zen.routerLink routerLink: internalRoutes.zen.routerLink
}, },
{ {
iconName: 'wallet-outline', iconName: 'wallet-outline',
label: internalRoutes.zen.subRoutes.holdings.title, label: internalRoutes.zen.subRoutes.holdings.title,
path: internalRoutes.zen.subRoutes.holdings.routerLink routerLink: internalRoutes.zen.subRoutes.holdings.routerLink
} }
]; ];
this.user = state.user; this.user = state.user;

2
apps/client/src/app/pages/zen/zen-page.html

@ -16,7 +16,7 @@
mat-tab-link mat-tab-link
routerLinkActive routerLinkActive
[active]="rla.isActive" [active]="rla.isActive"
[routerLink]="tab.path" [routerLink]="tab.routerLink"
[routerLinkActiveOptions]="{ exact: true }" [routerLinkActiveOptions]="{ exact: true }"
> >
<ion-icon <ion-icon

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

File diff suppressed because it is too large

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

File diff suppressed because it is too large

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

File diff suppressed because it is too large

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

File diff suppressed because it is too large

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

File diff suppressed because it is too large

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

File diff suppressed because it is too large

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

File diff suppressed because it is too large

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

File diff suppressed because it is too large

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

File diff suppressed because it is too large

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

File diff suppressed because it is too large

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

File diff suppressed because it is too large

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

File diff suppressed because it is too large

2
libs/common/src/lib/interfaces/tab-configuration.interface.ts

@ -1,6 +1,6 @@
export interface TabConfiguration { export interface TabConfiguration {
iconName: string; iconName: string;
label: string; label: string;
path: string[]; routerLink: string[];
showCondition?: boolean; showCondition?: boolean;
} }

243
libs/common/src/lib/routes/routes.ts

@ -2,54 +2,69 @@ import '@angular/localize/init';
import { IRoute } from './interfaces/interfaces'; import { IRoute } from './interfaces/interfaces';
export const routes = {
access: 'access',
adminControl: 'admin',
api: 'api',
auth: 'auth',
demo: 'demo',
i18n: 'i18n',
jobs: 'jobs',
market: 'market',
marketData: 'market-data',
membership: 'membership',
personalFinanceTools: 'personal-finance-tools',
public: 'p',
saas: 'saas',
settings: 'settings',
start: 'start',
users: 'users',
webauthn: 'webauthn',
// Publicly accessible pages
about: $localize`:kebab-case:about`,
blog: 'blog',
changelog: $localize`:kebab-case:changelog`,
faq: $localize`:kebab-case:faq`,
glossary: $localize`:kebab-case:glossary`,
guides: $localize`:kebab-case:guides`,
license: $localize`:kebab-case:license`,
markets: $localize`:kebab-case:markets`,
openSourceAlternativeTo: $localize`:kebab-case:open-source-alternative-to`,
ossFriends: 'oss-friends',
pricing: $localize`:kebab-case:pricing`,
privacyPolicy: $localize`:kebab-case:privacy-policy`,
resources: $localize`:kebab-case:resources`,
selfHosting: $localize`:kebab-case:self-hosting`,
termsOfService: $localize`:kebab-case:terms-of-service`
};
export const internalRoutes: Record<string, IRoute> = { export const internalRoutes: Record<string, IRoute> = {
account: { account: {
path: 'account', path: 'account',
routerLink: ['/account'], routerLink: ['/account'],
subRoutes: {
access: {
path: 'access',
routerLink: ['/account', 'access'],
title: $localize`Access`
},
membership: {
path: 'membership',
routerLink: ['/account', 'membership'],
title: $localize`Membership`
}
},
title: $localize`Settings` title: $localize`Settings`
}, },
adminControl: {
excludeFromAssistant: true,
path: 'admin',
routerLink: ['/admin'],
subRoutes: {
jobs: {
path: 'jobs',
routerLink: ['/admin', 'jobs'],
title: $localize`Job Queue`
},
marketData: {
path: 'market-data',
routerLink: ['/admin', 'market-data'],
title: $localize`Market Data`
},
settings: {
path: 'settings',
routerLink: ['/admin', 'settings'],
title: $localize`Settings`
},
users: {
path: 'users',
routerLink: ['/admin', 'users'],
title: $localize`Users`
}
},
title: $localize`Admin Control`
},
accounts: { accounts: {
path: 'accounts', path: 'accounts',
routerLink: ['/accounts'], routerLink: ['/accounts'],
title: $localize`Accounts` title: $localize`Accounts`
}, },
api: {
excludeFromAssistant: true,
path: 'api',
routerLink: ['/api'],
title: 'Ghostfolio API'
},
auth: {
excludeFromAssistant: true,
path: 'auth',
routerLink: ['/auth'],
title: $localize`Sign in`
},
home: { home: {
path: 'home', path: 'home',
routerLink: ['/home'], routerLink: ['/home'],
@ -59,6 +74,11 @@ export const internalRoutes: Record<string, IRoute> = {
routerLink: ['/home', 'holdings'], routerLink: ['/home', 'holdings'],
title: $localize`Holdings` title: $localize`Holdings`
}, },
markets: {
path: 'markets',
routerLink: ['/home', 'markets'],
title: $localize`Markets`
},
summary: { summary: {
path: 'summary', path: 'summary',
routerLink: ['/home', 'summary'], routerLink: ['/home', 'summary'],
@ -72,6 +92,12 @@ export const internalRoutes: Record<string, IRoute> = {
}, },
title: $localize`Overview` title: $localize`Overview`
}, },
i18n: {
excludeFromAssistant: true,
path: 'i18n',
routerLink: ['/i18n'],
title: $localize`Internationalization`
},
portfolio: { portfolio: {
path: 'portfolio', path: 'portfolio',
routerLink: ['/portfolio'], routerLink: ['/portfolio'],
@ -104,6 +130,12 @@ export const internalRoutes: Record<string, IRoute> = {
}, },
title: $localize`Portfolio` title: $localize`Portfolio`
}, },
webauthn: {
excludeFromAssistant: true,
path: 'webauthn',
routerLink: ['/webauthn'],
title: $localize`Sign in`
},
zen: { zen: {
excludeFromAssistant: true, excludeFromAssistant: true,
path: 'zen', path: 'zen',
@ -120,19 +152,156 @@ export const internalRoutes: Record<string, IRoute> = {
}; };
export const publicRoutes = { export const publicRoutes = {
about: {
path: $localize`:kebab-case:about`,
routerLink: ['/' + $localize`:kebab-case:about`],
subRoutes: {
changelog: {
path: $localize`:kebab-case:changelog`,
routerLink: [
'/' + $localize`:kebab-case:about`,
$localize`:kebab-case:changelog`
],
title: $localize`Changelog`
},
license: {
path: $localize`:kebab-case:license`,
routerLink: [
'/' + $localize`:kebab-case:about`,
$localize`:kebab-case:license`
],
title: $localize`License`
},
ossFriends: {
path: 'oss-friends',
routerLink: ['/' + $localize`:kebab-case:about`, 'oss-friends'],
title: 'OSS Friends'
},
privacyPolicy: {
path: $localize`:kebab-case:privacy-policy`,
routerLink: [
'/' + $localize`:kebab-case:about`,
$localize`:kebab-case:privacy-policy`
],
title: $localize`Privacy Policy`
},
termsOfService: {
path: $localize`:kebab-case:terms-of-service`,
routerLink: [
'/' + $localize`:kebab-case:about`,
$localize`:kebab-case:terms-of-service`
],
title: $localize`Terms of Service`
}
},
title: $localize`About`
},
blog: {
path: 'blog',
routerLink: ['/blog'],
title: $localize`Blog`
},
demo: {
path: 'demo',
routerLink: ['/demo'],
title: $localize`Live Demo`
},
faq: {
path: $localize`:kebab-case:faq`,
routerLink: ['/' + $localize`:kebab-case:faq`],
subRoutes: {
saas: {
path: 'saas',
routerLink: ['/' + $localize`:kebab-case:faq`, 'saas'],
title: $localize`Cloud` + ' (SaaS)'
},
selfHosting: {
path: $localize`:kebab-case:self-hosting`,
routerLink: [
'/' + $localize`:kebab-case:faq`,
$localize`:kebab-case:self-hosting`
],
title: $localize`Self-Hosting`
}
},
title: $localize`Frequently Asked Questions (FAQ)`
},
features: { features: {
path: $localize`:kebab-case:features`, path: $localize`:kebab-case:features`,
routerLink: ['/' + $localize`:kebab-case:features`], routerLink: ['/' + $localize`:kebab-case:features`],
title: $localize`Features` title: $localize`Features`
}, },
markets: {
path: $localize`:kebab-case:markets`,
routerLink: ['/' + $localize`:kebab-case:markets`],
title: $localize`Markets`
},
openStartup: { openStartup: {
path: 'open', path: 'open',
routerLink: ['/open'], routerLink: ['/open'],
title: 'Open Startup' title: 'Open Startup'
}, },
pricing: {
path: $localize`:kebab-case:pricing`,
routerLink: ['/' + $localize`:kebab-case:pricing`],
title: $localize`Pricing`
},
public: {
path: 'p',
routerLink: ['/p']
},
register: { register: {
path: $localize`:kebab-case:register`, path: $localize`:kebab-case:register`,
routerLink: ['/' + $localize`:kebab-case:register`], routerLink: ['/' + $localize`:kebab-case:register`],
title: $localize`Registration` title: $localize`Registration`
},
resources: {
path: $localize`:kebab-case:resources`,
routerLink: ['/' + $localize`:kebab-case:resources`],
subRoutes: {
glossary: {
path: $localize`:kebab-case:glossary`,
routerLink: [
'/' + $localize`:kebab-case:resources`,
$localize`:kebab-case:glossary`
],
title: $localize`Glossary`
},
guides: {
path: $localize`:kebab-case:guides`,
routerLink: [
'/' + $localize`:kebab-case:resources`,
$localize`:kebab-case:guides`
],
title: $localize`Guides`
},
markets: {
path: $localize`:kebab-case:markets`,
routerLink: [
'/' + $localize`:kebab-case:resources`,
$localize`:kebab-case:markets`
],
title: $localize`Markets`
},
personalFinanceTools: {
path: 'personal-finance-tools',
routerLink: [
'/' + $localize`:kebab-case:resources`,
'personal-finance-tools'
],
subRoutes: {
product: {
path: $localize`:kebab-case:open-source-alternative-to`,
title: $localize`Open Source Alternative to`
}
},
title: $localize`Personal Finance Tools`
}
},
title: $localize`Resources`
},
start: {
path: 'start',
routerLink: ['/start']
} }
}; };

5
libs/ui/src/lib/account-balances/account-balances.component.ts

@ -2,7 +2,7 @@ import { CreateAccountBalanceDto } from '@ghostfolio/api/app/account-balance/cre
import { ConfirmationDialogType } from '@ghostfolio/client/core/notification/confirmation-dialog/confirmation-dialog.type'; import { ConfirmationDialogType } from '@ghostfolio/client/core/notification/confirmation-dialog/confirmation-dialog.type';
import { NotificationService } from '@ghostfolio/client/core/notification/notification.service'; import { NotificationService } from '@ghostfolio/client/core/notification/notification.service';
import { validateObjectForForm } from '@ghostfolio/client/util/form.util'; import { validateObjectForForm } from '@ghostfolio/client/util/form.util';
import { getLocale } from '@ghostfolio/common/helper'; import { DATE_FORMAT, getLocale } from '@ghostfolio/common/helper';
import { AccountBalancesResponse } from '@ghostfolio/common/interfaces'; import { AccountBalancesResponse } from '@ghostfolio/common/interfaces';
import { import {
@ -31,6 +31,7 @@ import { MatInputModule } from '@angular/material/input';
import { MatMenuModule } from '@angular/material/menu'; import { MatMenuModule } from '@angular/material/menu';
import { MatSort, MatSortModule } from '@angular/material/sort'; import { MatSort, MatSortModule } from '@angular/material/sort';
import { MatTableDataSource, MatTableModule } from '@angular/material/table'; import { MatTableDataSource, MatTableModule } from '@angular/material/table';
import { format } from 'date-fns';
import { get } from 'lodash'; import { get } from 'lodash';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
@ -114,7 +115,7 @@ export class GfAccountBalancesComponent
const accountBalance: CreateAccountBalanceDto = { const accountBalance: CreateAccountBalanceDto = {
accountId: this.accountId, accountId: this.accountId,
balance: this.accountBalanceForm.get('balance').value, balance: this.accountBalanceForm.get('balance').value,
date: this.accountBalanceForm.get('date').value.toISOString() date: format(this.accountBalanceForm.get('date').value, DATE_FORMAT)
}; };
try { try {

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save