diff --git a/CHANGELOG.md b/CHANGELOG.md index ece8033a81..8c07018bea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Added + +- Added a blog post: _Black Weeks 2026_ + ### Changed - Extended the sectors mapping in the data enhancer for asset profile data via _Trackinsight_ diff --git a/apps/api/src/app/endpoints/sitemap/sitemap.service.ts b/apps/api/src/app/endpoints/sitemap/sitemap.service.ts index cf4b5052fb..6b3a99a8ba 100644 --- a/apps/api/src/app/endpoints/sitemap/sitemap.service.ts +++ b/apps/api/src/app/endpoints/sitemap/sitemap.service.ts @@ -124,6 +124,10 @@ export class SitemapService { { languageCode: 'en', routerLink: ['2026', '04', 'ghostfolio-3'] + }, + { + languageCode: 'en', + routerLink: ['2026', '11', 'black-weeks-2026'] } ] .map(({ languageCode, routerLink }) => { diff --git a/apps/api/src/middlewares/html-template.middleware.ts b/apps/api/src/middlewares/html-template.middleware.ts index 928a9f22c5..09e9af68f8 100644 --- a/apps/api/src/middlewares/html-template.middleware.ts +++ b/apps/api/src/middlewares/html-template.middleware.ts @@ -87,6 +87,12 @@ const locales = { '/en/blog/2026/04/ghostfolio-3': { featureGraphicPath: 'assets/images/blog/ghostfolio-3.jpg', title: `Announcing Ghostfolio 3.0 - ${title}` + }, + '/en/blog/2026/11/black-weeks-2026': { + description: + 'Get 15 months of Ghostfolio Premium for the price of 12. Track stocks, ETFs and crypto with the open source portfolio tracker. Offer valid until the end of November.', + featureGraphicPath: 'assets/images/blog/black-weeks-2026.jpg', + title: `Black Weeks 2026: 15 Months of Premium - ${title}` } }; @@ -156,10 +162,12 @@ export class HtmlTemplateMiddleware implements NestMiddleware { languageCode, path, rootUrl, - description: this.i18nService.getTranslation({ - languageCode, - id: 'metaDescription' - }), + description: + locales[path]?.description ?? + this.i18nService.getTranslation({ + languageCode, + id: 'metaDescription' + }), featureGraphicPath: locales[path]?.featureGraphicPath ?? 'assets/cover.png', keywords: this.i18nService.getTranslation({ diff --git a/apps/client/src/app/pages/blog/2024/11/black-weeks-2024/black-weeks-2024-page.html b/apps/client/src/app/pages/blog/2024/11/black-weeks-2024/black-weeks-2024-page.html index 9060b2f9a3..923e80f0d1 100644 --- a/apps/client/src/app/pages/blog/2024/11/black-weeks-2024/black-weeks-2024-page.html +++ b/apps/client/src/app/pages/blog/2024/11/black-weeks-2024/black-weeks-2024-page.html @@ -12,6 +12,13 @@ title="Black Weeks 2024" /> +
+

+ This offer has ended. See + Black Weeks 2026 + for our latest offer. +

+

Take advantage of our exclusive Black Weeks offer diff --git a/apps/client/src/app/pages/blog/2025/11/black-weeks-2025/black-weeks-2025-page.html b/apps/client/src/app/pages/blog/2025/11/black-weeks-2025/black-weeks-2025-page.html index 33b35077eb..df05146b5f 100644 --- a/apps/client/src/app/pages/blog/2025/11/black-weeks-2025/black-weeks-2025-page.html +++ b/apps/client/src/app/pages/blog/2025/11/black-weeks-2025/black-weeks-2025-page.html @@ -12,6 +12,13 @@ title="Black Weeks 2025" /> +

+

+ This offer has ended. See + Black Weeks 2026 + for our latest offer. +

+

Save 25% on the diff --git a/apps/client/src/app/pages/blog/2026/11/black-weeks-2026/black-weeks-2026-page.component.ts b/apps/client/src/app/pages/blog/2026/11/black-weeks-2026/black-weeks-2026-page.component.ts new file mode 100644 index 0000000000..7e53acf955 --- /dev/null +++ b/apps/client/src/app/pages/blog/2026/11/black-weeks-2026/black-weeks-2026-page.component.ts @@ -0,0 +1,76 @@ +import { UserService } from '@ghostfolio/client/services/user/user.service'; +import { SubscriptionType } from '@ghostfolio/common/enums'; +import { SubscriptionOffer } from '@ghostfolio/common/interfaces'; +import { publicRoutes } from '@ghostfolio/common/routes/routes'; +import { GfPremiumIndicatorComponent } from '@ghostfolio/ui/premium-indicator'; +import { DataService } from '@ghostfolio/ui/services'; + +import { DecimalPipe } from '@angular/common'; +import { + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + DestroyRef, + inject, + OnInit +} from '@angular/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { MatButtonModule } from '@angular/material/button'; +import { RouterModule } from '@angular/router'; + +@Component({ + changeDetection: ChangeDetectionStrategy.OnPush, + host: { class: 'page' }, + imports: [ + DecimalPipe, + GfPremiumIndicatorComponent, + MatButtonModule, + RouterModule + ], + selector: 'gf-black-weeks-2026-page', + templateUrl: './black-weeks-2026-page.html' +}) +export class BlackWeeks2026PageComponent implements OnInit { + public baseCurrency: string; + public price: number | undefined; + public routerLinkBlog = publicRoutes.blog.routerLink; + public routerLinkFeatures = publicRoutes.features.routerLink; + public routerLinkPricing = publicRoutes.pricing.routerLink; + + private readonly changeDetectorRef = inject(ChangeDetectorRef); + private readonly dataService = inject(DataService); + private readonly destroyRef = inject(DestroyRef); + private readonly userService = inject(UserService); + + public ngOnInit() { + const { baseCurrency, subscriptionOffer } = this.dataService.fetchInfo(); + + this.baseCurrency = baseCurrency; + this.setPrice(subscriptionOffer); + + this.userService.stateChanged + .pipe(takeUntilDestroyed(this.destroyRef)) + .subscribe((state) => { + if (state?.user) { + this.setPrice( + state.user.subscription?.offer, + state.user.subscription?.type + ); + } + + this.changeDetectorRef.markForCheck(); + }); + } + + private setPrice( + offer: SubscriptionOffer | undefined, + subscriptionType?: SubscriptionType + ) { + this.price = + offer?.durationExtension && + offer.price && + subscriptionType !== SubscriptionType.Premium + ? offer.price - (offer.coupon ?? 0) + : undefined; + } +} diff --git a/apps/client/src/app/pages/blog/2026/11/black-weeks-2026/black-weeks-2026-page.html b/apps/client/src/app/pages/blog/2026/11/black-weeks-2026/black-weeks-2026-page.html new file mode 100644 index 0000000000..473ef58a8e --- /dev/null +++ b/apps/client/src/app/pages/blog/2026/11/black-weeks-2026/black-weeks-2026-page.html @@ -0,0 +1,201 @@ +

+
+
+
+
+

+ Black Weeks 2026: 15 Months of Premium for the Price of 12 +

+
2026-11-01
+ Black Weeks 2026: 15 months of Ghostfolio Premium for the price of 12 +
+
+

+ Get 15 months of + Ghostfolio Premium + + + for the price of 12. Our Black Weeks offer adds + 3 extra months to the annual plan of the open + source portfolio tracker, which gives you 25% more time at the same + price. +

+
+
+

What you get with Ghostfolio Premium

+

+ Ghostfolio + unifies your finances in one place and gives you a clear overview of + your portfolio across stocks, ETFs, cryptocurrencies or other + assets. With Ghostfolio Premium, you can: +

+
    +
  • + See the full picture: Understand your portfolio + at a glance with the summary and allocations. +
  • +
  • + Measure your progress: Compare your performance + against benchmarks. +
  • +
  • + Access professional data: Get 80’000+ tickers + from over 50 exchanges. +
  • +
  • + Plan your financial independence: Calculate your + path to retirement with the FIRE calculator. +
  • +
  • + Connect your AI client: Analyze your portfolio + and import activities with the new Ghostfolio MCP (enable + Experimental Features in your account settings). +
  • +
+

+ Explore all the + features of Ghostfolio. +

+
+
+

How the offer works

+ @if (price) { +

+ You pay {{ baseCurrency }} {{ price }} for + 15 months, which is {{ baseCurrency }} {{ + price / 15 | number: '1.2-2' + }} + per month. +

+ } +

+ It is a one-time payment with no auto-renewal. The offer is valid + until the end of November. +

+ @if (price) { +

+ Get 3 extra months +

+

+ More details are available on the + pricing page. +

+ } +
+
+
    +
  • + 2026 +
  • +
  • + Black Friday +
  • +
  • + Black Weeks +
  • +
  • + Cryptocurrency +
  • +
  • + Deal +
  • +
  • + ETF +
  • +
  • + Finance +
  • +
  • + Fintech +
  • +
  • + Ghostfolio +
  • +
  • + Ghostfolio MCP +
  • +
  • + Ghostfolio Premium +
  • +
  • + Investment +
  • +
  • + Open Source +
  • +
  • + OSS +
  • +
  • + Personal Finance +
  • +
  • + Portfolio +
  • +
  • + Portfolio Tracker +
  • +
  • + Pricing +
  • +
  • + Promotion +
  • +
  • + SaaS +
  • +
  • + Sale +
  • +
  • + Software +
  • +
  • + Stock +
  • +
  • + Subscription +
  • +
  • + Wealth +
  • +
  • + Wealth Management +
  • +
+
+ +
+
+
+
diff --git a/apps/client/src/app/pages/blog/blog-page.html b/apps/client/src/app/pages/blog/blog-page.html index 3a09b108d4..10da38d343 100644 --- a/apps/client/src/app/pages/blog/blog-page.html +++ b/apps/client/src/app/pages/blog/blog-page.html @@ -8,6 +8,32 @@ finance + @if (hasPermissionForSubscription) { + + + + + + }
diff --git a/apps/client/src/app/pages/blog/blog-page.routes.ts b/apps/client/src/app/pages/blog/blog-page.routes.ts index 9afb35d464..114712a582 100644 --- a/apps/client/src/app/pages/blog/blog-page.routes.ts +++ b/apps/client/src/app/pages/blog/blog-page.routes.ts @@ -227,5 +227,14 @@ export const routes: Routes = [ (c) => c.Ghostfolio3PageComponent ), title: 'Ghostfolio 3.0' + }, + { + canActivate: [AuthGuard], + path: '2026/11/black-weeks-2026', + loadComponent: () => + import('./2026/11/black-weeks-2026/black-weeks-2026-page.component').then( + (c) => c.BlackWeeks2026PageComponent + ), + title: 'Black Weeks 2026: 15 Months of Premium' } ]; diff --git a/apps/client/src/assets/images/blog/black-weeks-2026.jpg b/apps/client/src/assets/images/blog/black-weeks-2026.jpg new file mode 100644 index 0000000000..c71827c5ef Binary files /dev/null and b/apps/client/src/assets/images/blog/black-weeks-2026.jpg differ