From 82fdf826f8113c9a717bf319bbb0f427a55787b3 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Mon, 2 Nov 2026 20:22:19 +0100 Subject: [PATCH] Feature/Max plan on pricing page (#8376) * Setup Ghostfolio Max plan * Update changelog --- CHANGELOG.md | 1 + .../src/app/endpoints/info/info.service.ts | 7 +- .../pages/pricing/pricing-page.component.ts | 12 +- .../src/app/pages/pricing/pricing-page.html | 358 ++++++++++-------- libs/common/src/lib/config.ts | 19 + .../src/lib/interfaces/info-item.interface.ts | 1 + .../lib/types/subscription-offer-key.type.ts | 1 + 7 files changed, 243 insertions(+), 156 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a792bb55e..2f63aac4b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Improved the content of the pricing page - Upgraded `stripe` from version `23.0.0` to `23.0.1` ### Fixed diff --git a/apps/api/src/app/endpoints/info/info.service.ts b/apps/api/src/app/endpoints/info/info.service.ts index 666b11dd1c..ecff1c16b5 100644 --- a/apps/api/src/app/endpoints/info/info.service.ts +++ b/apps/api/src/app/endpoints/info/info.service.ts @@ -109,14 +109,16 @@ export class InfoService { isUserSignupEnabled, latestFearAndGreedStocksMarketData, statistics, - subscriptionOffer + subscriptionOffer, + subscriptionOfferMax ] = await Promise.all([ this.benchmarkService.getBenchmarkAssetProfiles(), this.getDemoAuthToken(), this.propertyService.isUserSignupEnabled(), latestFearAndGreedStocksMarketDataPromise, this.getStatistics(), - this.subscriptionService.getSubscriptionOffer({ key: 'default' }) + this.subscriptionService.getSubscriptionOffer({ key: 'default' }), + this.subscriptionService.getSubscriptionOffer({ key: 'max' }) ]); if (isUserSignupEnabled) { @@ -131,6 +133,7 @@ export class InfoService { isReadOnlyMode, statistics, subscriptionOffer, + subscriptionOfferMax, baseCurrency: DEFAULT_CURRENCY, currencies: this.exchangeRateDataService.getCurrencies(), fearAndGreedStocksMarketPrice: diff --git a/apps/client/src/app/pages/pricing/pricing-page.component.ts b/apps/client/src/app/pages/pricing/pricing-page.component.ts index 8b4e07ffb7..4a2fd22a7b 100644 --- a/apps/client/src/app/pages/pricing/pricing-page.component.ts +++ b/apps/client/src/app/pages/pricing/pricing-page.component.ts @@ -1,4 +1,5 @@ import { UserService } from '@ghostfolio/client/services/user/user.service'; +import { MAX_PLAN_MAIL_HREF } from '@ghostfolio/common/config'; import { User } from '@ghostfolio/common/interfaces'; import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { publicRoutes } from '@ghostfolio/common/routes/routes'; @@ -64,7 +65,9 @@ export class GfPricingPageComponent implements OnInit { ); protected label: string | undefined; + protected readonly maxPlanMailHref = MAX_PLAN_MAIL_HREF; protected price: number | undefined; + protected priceMax: number | undefined; protected readonly professionalDataProviderTooltipPremium = translate( 'PROFESSIONAL_DATA_PROVIDER_TOOLTIP_PREMIUM' @@ -92,8 +95,12 @@ export class GfPricingPageComponent implements OnInit { } public ngOnInit() { - const { baseCurrency, globalPermissions, subscriptionOffer } = - this.dataService.fetchInfo(); + const { + baseCurrency, + globalPermissions, + subscriptionOffer, + subscriptionOfferMax + } = this.dataService.fetchInfo(); this.baseCurrency = baseCurrency; this.coupon = subscriptionOffer?.coupon; @@ -106,6 +113,7 @@ export class GfPricingPageComponent implements OnInit { this.label = subscriptionOffer?.label; this.price = subscriptionOffer?.price; + this.priceMax = subscriptionOfferMax?.price; this.userService.stateChanged .pipe(takeUntilDestroyed(this.destroyRef)) diff --git a/apps/client/src/app/pages/pricing/pricing-page.html b/apps/client/src/app/pages/pricing/pricing-page.html index a8b36ea0e8..7cdaf6c89f 100644 --- a/apps/client/src/app/pages/pricing/pricing-page.html +++ b/apps/client/src/app/pages/pricing/pricing-page.html @@ -10,99 +10,8 @@ hosting infrastructure and professional data providers, and to fund ongoing development.
-- If you prefer to run Ghostfolio on your own infrastructure, please - find the source code and further instructions on - GitHub. -
-- For tech-savvy investors who prefer to run Ghostfolio on their - own infrastructure. -
-Self-hosted, update manually.
-Free
- @if (user?.subscription?.type === 'Basic') { - - } - @if (durationExtension) { -Fully managed Ghostfolio cloud offering.
Free
@if (user?.subscription?.type === 'Basic') {Fully managed Ghostfolio cloud offering.
@if (price) {@@ -314,65 +223,210 @@
For investors who manage several portfolios.
++ + {{ baseCurrency }} {{ priceMax }} + + per year +
+ @if ( + hasPermissionToUpdateUserSettings && + user?.subscription?.type === 'Basic' + ) { ++ +
+
-
+
It’s free.
++ It’s free. +
++ For tech-savvy investors who prefer to run Ghostfolio on their + own infrastructure. +
+Self-hosted, update manually.
+Free
++ Please find the source code and further instructions on + GitHub. +
+