Browse Source

Merge remote-tracking branch 'upstream/main' into feature/public-access-filter

pull/5848/head
Germán Martín 4 weeks ago
parent
commit
4f0da5dbef
  1. 4
      CHANGELOG.md
  2. 4
      apps/api/src/app/endpoints/sitemap/sitemap.service.ts
  3. 2
      apps/api/src/app/subscription/subscription.service.ts
  4. 4
      apps/api/src/middlewares/html-template.middleware.ts
  5. 7
      apps/api/src/services/data-provider/financial-modeling-prep/financial-modeling-prep.service.ts
  6. 2
      apps/client/src/app/components/access-table/access-table.component.ts
  7. 4
      apps/client/src/app/components/account-detail-dialog/account-detail-dialog.component.ts
  8. 2
      apps/client/src/app/components/admin-market-data/admin-market-data.service.ts
  9. 2
      apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts
  10. 2
      apps/client/src/app/components/admin-overview/admin-overview.component.ts
  11. 2
      apps/client/src/app/components/admin-platform/admin-platform.component.ts
  12. 2
      apps/client/src/app/components/admin-platform/create-or-update-platform-dialog/create-or-update-platform-dialog.component.ts
  13. 2
      apps/client/src/app/components/admin-settings/admin-settings.component.ts
  14. 2
      apps/client/src/app/components/admin-tag/admin-tag.component.ts
  15. 2
      apps/client/src/app/components/admin-tag/create-or-update-tag-dialog/create-or-update-tag-dialog.component.ts
  16. 2
      apps/client/src/app/components/admin-users/admin-users.component.ts
  17. 4
      apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts
  18. 2
      apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.component.ts
  19. 2
      apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.component.ts
  20. 2
      apps/client/src/app/components/user-account-access/user-account-access.component.ts
  21. 2
      apps/client/src/app/components/user-account-membership/user-account-membership.component.ts
  22. 2
      apps/client/src/app/components/user-account-settings/user-account-settings.component.ts
  23. 4
      apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts
  24. 3
      apps/client/src/app/core/notification/confirmation-dialog/confirmation-dialog.component.ts
  25. 2
      apps/client/src/app/core/notification/confirmation-dialog/interfaces/interfaces.ts
  26. 2
      apps/client/src/app/core/notification/interfaces/interfaces.ts
  27. 2
      apps/client/src/app/core/notification/notification.service.ts
  28. 2
      apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.component.ts
  29. 18
      apps/client/src/app/pages/blog/2025/11/black-weeks-2025/black-weeks-2025-page.component.ts
  30. 159
      apps/client/src/app/pages/blog/2025/11/black-weeks-2025/black-weeks-2025-page.html
  31. 26
      apps/client/src/app/pages/blog/blog-page.html
  32. 9
      apps/client/src/app/pages/blog/blog-page.routes.ts
  33. 2
      apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.component.ts
  34. 4
      apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts
  35. BIN
      apps/client/src/assets/images/blog/black-weeks-2025.jpg
  36. 1
      apps/client/src/polyfills.ts
  37. 0
      libs/common/src/lib/enums/confirmation-dialog.type.ts
  38. 4
      libs/common/src/lib/enums/index.ts
  39. 0
      libs/common/src/lib/enums/subscription-type.type.ts
  40. 2
      libs/common/src/lib/interfaces/system-message.interface.ts
  41. 6
      libs/common/src/lib/interfaces/user.interface.ts
  42. 2
      libs/common/src/lib/types/index.ts
  43. 2
      libs/common/src/lib/types/user-with-settings.type.ts
  44. 0
      libs/common/src/lib/utils/form.util.ts
  45. 3
      libs/common/src/lib/utils/index.ts
  46. 4
      libs/ui/src/lib/account-balances/account-balances.component.ts
  47. 2
      libs/ui/src/lib/accounts-table/accounts-table.component.ts
  48. 2
      libs/ui/src/lib/activities-table/activities-table.component.ts
  49. 4
      libs/ui/src/lib/benchmark/benchmark-detail-dialog/benchmark-detail-dialog.component.ts
  50. 2
      libs/ui/src/lib/benchmark/benchmark.component.ts
  51. 0
      libs/ui/src/lib/dialog-footer/dialog-footer.component.html
  52. 0
      libs/ui/src/lib/dialog-footer/dialog-footer.component.scss
  53. 0
      libs/ui/src/lib/dialog-footer/dialog-footer.component.ts
  54. 1
      libs/ui/src/lib/dialog-footer/index.ts
  55. 0
      libs/ui/src/lib/dialog-header/dialog-header.component.html
  56. 0
      libs/ui/src/lib/dialog-header/dialog-header.component.scss
  57. 0
      libs/ui/src/lib/dialog-header/dialog-header.component.ts
  58. 1
      libs/ui/src/lib/dialog-header/index.ts
  59. 4
      package-lock.json
  60. 2
      package.json

4
CHANGELOG.md

@ -5,17 +5,19 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Unreleased
## 2.217.1 - 2025-11-16
### Added
- Introduced support for automatically gathering required exchange rates, exposed as an environment variable (`ENABLE_FEATURE_GATHER_NEW_EXCHANGE_RATES`)
- Added a blog post: _Black Weeks 2025_
### Changed
- Refactored the get holding functionality in the portfolio service
- Changed the user data loading in the user detail dialog of the admin control panel’s users section to fetch data on demand
- Exposed the authentication with access token as an environment variable (`ENABLE_FEATURE_AUTH_TOKEN`)
- Improved the search functionality of the _Financial Modeling Prep_ service
- Improved the language localization for German (`de`)
- Upgraded `prisma` from version `6.18.0` to `6.19.0`

4
apps/api/src/app/endpoints/sitemap/sitemap.service.ts

@ -116,6 +116,10 @@ export class SitemapService {
{
languageCode: 'en',
routerLink: ['2025', '09', 'hacktoberfest-2025']
},
{
languageCode: 'en',
routerLink: ['2025', '11', 'black-weeks-2025']
}
]
.map(({ languageCode, routerLink }) => {

2
apps/api/src/app/subscription/subscription.service.ts

@ -5,6 +5,7 @@ import {
DEFAULT_LANGUAGE_CODE,
PROPERTY_STRIPE_CONFIG
} from '@ghostfolio/common/config';
import { SubscriptionType } from '@ghostfolio/common/enums';
import { parseDate } from '@ghostfolio/common/helper';
import {
CreateStripeCheckoutSessionResponse,
@ -14,7 +15,6 @@ import {
SubscriptionOfferKey,
UserWithSettings
} from '@ghostfolio/common/types';
import { SubscriptionType } from '@ghostfolio/common/types/subscription-type.type';
import { Injectable, Logger } from '@nestjs/common';
import { Subscription } from '@prisma/client';

4
apps/api/src/middlewares/html-template.middleware.ts

@ -79,6 +79,10 @@ const locales = {
'/en/blog/2025/09/hacktoberfest-2025': {
featureGraphicPath: 'assets/images/blog/hacktoberfest-2025.png',
title: `Hacktoberfest 2025 - ${title}`
},
'/en/blog/2025/11/black-weeks-2025': {
featureGraphicPath: 'assets/images/blog/black-weeks-2025.jpg',
title: `Black Weeks 2025 - ${title}`
}
};

7
apps/api/src/services/data-provider/financial-modeling-prep/financial-modeling-prep.service.ts

@ -504,8 +504,11 @@ export class FinancialModelingPrepService implements DataProviderInterface {
).then((res) => res.json());
items = result
.filter(({ symbol }) => {
if (includeIndices === false && symbol.startsWith('^')) {
.filter(({ exchange, symbol }) => {
if (
exchange === 'FOREX' ||
(includeIndices === false && symbol.startsWith('^'))
) {
return false;
}

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

@ -1,5 +1,5 @@
import { ConfirmationDialogType } from '@ghostfolio/client/core/notification/confirmation-dialog/confirmation-dialog.type';
import { NotificationService } from '@ghostfolio/client/core/notification/notification.service';
import { ConfirmationDialogType } from '@ghostfolio/common/enums';
import { Access, User } from '@ghostfolio/common/interfaces';
import { publicRoutes } from '@ghostfolio/common/routes/routes';

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

@ -1,5 +1,3 @@
import { GfDialogFooterComponent } from '@ghostfolio/client/components/dialog-footer/dialog-footer.component';
import { GfDialogHeaderComponent } from '@ghostfolio/client/components/dialog-header/dialog-header.component';
import { GfInvestmentChartComponent } from '@ghostfolio/client/components/investment-chart/investment-chart.component';
import { DataService } from '@ghostfolio/client/services/data.service';
import { UserService } from '@ghostfolio/client/services/user/user.service';
@ -18,6 +16,8 @@ import { internalRoutes } from '@ghostfolio/common/routes/routes';
import { OrderWithAccount } from '@ghostfolio/common/types';
import { GfAccountBalancesComponent } from '@ghostfolio/ui/account-balances';
import { GfActivitiesTableComponent } from '@ghostfolio/ui/activities-table';
import { GfDialogFooterComponent } from '@ghostfolio/ui/dialog-footer';
import { GfDialogHeaderComponent } from '@ghostfolio/ui/dialog-header';
import { GfHoldingsTableComponent } from '@ghostfolio/ui/holdings-table';
import { GfValueComponent } from '@ghostfolio/ui/value';

2
apps/client/src/app/components/admin-market-data/admin-market-data.service.ts

@ -1,7 +1,7 @@
import { ConfirmationDialogType } from '@ghostfolio/client/core/notification/confirmation-dialog/confirmation-dialog.type';
import { NotificationService } from '@ghostfolio/client/core/notification/notification.service';
import { AdminService } from '@ghostfolio/client/services/admin.service';
import { ghostfolioScraperApiSymbolPrefix } from '@ghostfolio/common/config';
import { ConfirmationDialogType } from '@ghostfolio/common/enums';
import {
getCurrencyFromSymbol,
isDerivedCurrency,

2
apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts

@ -3,7 +3,6 @@ import { NotificationService } from '@ghostfolio/client/core/notification/notifi
import { AdminService } from '@ghostfolio/client/services/admin.service';
import { DataService } from '@ghostfolio/client/services/data.service';
import { UserService } from '@ghostfolio/client/services/user/user.service';
import { validateObjectForForm } from '@ghostfolio/client/util/form.util';
import {
ASSET_CLASS_MAPPING,
PROPERTY_IS_DATA_GATHERING_ENABLED
@ -19,6 +18,7 @@ import {
User
} from '@ghostfolio/common/interfaces';
import { DateRange } from '@ghostfolio/common/types';
import { validateObjectForForm } from '@ghostfolio/common/utils';
import { GfCurrencySelectorComponent } from '@ghostfolio/ui/currency-selector';
import { GfEntityLogoComponent } from '@ghostfolio/ui/entity-logo';
import { GfHistoricalMarketDataEditorComponent } from '@ghostfolio/ui/historical-market-data-editor';

2
apps/client/src/app/components/admin-overview/admin-overview.component.ts

@ -1,4 +1,3 @@
import { ConfirmationDialogType } from '@ghostfolio/client/core/notification/confirmation-dialog/confirmation-dialog.type';
import { NotificationService } from '@ghostfolio/client/core/notification/notification.service';
import { AdminService } from '@ghostfolio/client/services/admin.service';
import { CacheService } from '@ghostfolio/client/services/cache.service';
@ -12,6 +11,7 @@ import {
PROPERTY_SYSTEM_MESSAGE,
ghostfolioPrefix
} from '@ghostfolio/common/config';
import { ConfirmationDialogType } from '@ghostfolio/common/enums';
import { getDateFnsLocale } from '@ghostfolio/common/helper';
import {
Coupon,

2
apps/client/src/app/components/admin-platform/admin-platform.component.ts

@ -1,9 +1,9 @@
import { ConfirmationDialogType } from '@ghostfolio/client/core/notification/confirmation-dialog/confirmation-dialog.type';
import { NotificationService } from '@ghostfolio/client/core/notification/notification.service';
import { AdminService } from '@ghostfolio/client/services/admin.service';
import { DataService } from '@ghostfolio/client/services/data.service';
import { UserService } from '@ghostfolio/client/services/user/user.service';
import { CreatePlatformDto, UpdatePlatformDto } from '@ghostfolio/common/dtos';
import { ConfirmationDialogType } from '@ghostfolio/common/enums';
import { GfEntityLogoComponent } from '@ghostfolio/ui/entity-logo';
import {

2
apps/client/src/app/components/admin-platform/create-or-update-platform-dialog/create-or-update-platform-dialog.component.ts

@ -1,5 +1,5 @@
import { validateObjectForForm } from '@ghostfolio/client/util/form.util';
import { CreatePlatformDto, UpdatePlatformDto } from '@ghostfolio/common/dtos';
import { validateObjectForForm } from '@ghostfolio/common/utils';
import { GfEntityLogoComponent } from '@ghostfolio/ui/entity-logo';
import {

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

@ -1,12 +1,12 @@
import { GfAdminPlatformComponent } from '@ghostfolio/client/components/admin-platform/admin-platform.component';
import { GfAdminTagComponent } from '@ghostfolio/client/components/admin-tag/admin-tag.component';
import { GfDataProviderStatusComponent } from '@ghostfolio/client/components/data-provider-status/data-provider-status.component';
import { ConfirmationDialogType } from '@ghostfolio/client/core/notification/confirmation-dialog/confirmation-dialog.type';
import { NotificationService } from '@ghostfolio/client/core/notification/notification.service';
import { AdminService } from '@ghostfolio/client/services/admin.service';
import { DataService } from '@ghostfolio/client/services/data.service';
import { UserService } from '@ghostfolio/client/services/user/user.service';
import { PROPERTY_API_KEY_GHOSTFOLIO } from '@ghostfolio/common/config';
import { ConfirmationDialogType } from '@ghostfolio/common/enums';
import { getDateFormatString } from '@ghostfolio/common/helper';
import {
DataProviderGhostfolioStatusResponse,

2
apps/client/src/app/components/admin-tag/admin-tag.component.ts

@ -1,8 +1,8 @@
import { ConfirmationDialogType } from '@ghostfolio/client/core/notification/confirmation-dialog/confirmation-dialog.type';
import { NotificationService } from '@ghostfolio/client/core/notification/notification.service';
import { DataService } from '@ghostfolio/client/services/data.service';
import { UserService } from '@ghostfolio/client/services/user/user.service';
import { CreateTagDto, UpdateTagDto } from '@ghostfolio/common/dtos';
import { ConfirmationDialogType } from '@ghostfolio/common/enums';
import {
ChangeDetectionStrategy,

2
apps/client/src/app/components/admin-tag/create-or-update-tag-dialog/create-or-update-tag-dialog.component.ts

@ -1,5 +1,5 @@
import { validateObjectForForm } from '@ghostfolio/client/util/form.util';
import { CreateTagDto, UpdateTagDto } from '@ghostfolio/common/dtos';
import { validateObjectForForm } from '@ghostfolio/common/utils';
import {
ChangeDetectionStrategy,

2
apps/client/src/app/components/admin-users/admin-users.component.ts

@ -1,6 +1,5 @@
import { UserDetailDialogParams } from '@ghostfolio/client/components/user-detail-dialog/interfaces/interfaces';
import { GfUserDetailDialogComponent } from '@ghostfolio/client/components/user-detail-dialog/user-detail-dialog.component';
import { ConfirmationDialogType } from '@ghostfolio/client/core/notification/confirmation-dialog/confirmation-dialog.type';
import { NotificationService } from '@ghostfolio/client/core/notification/notification.service';
import { AdminService } from '@ghostfolio/client/services/admin.service';
import { DataService } from '@ghostfolio/client/services/data.service';
@ -8,6 +7,7 @@ import { ImpersonationStorageService } from '@ghostfolio/client/services/imperso
import { TokenStorageService } from '@ghostfolio/client/services/token-storage.service';
import { UserService } from '@ghostfolio/client/services/user/user.service';
import { DEFAULT_PAGE_SIZE } from '@ghostfolio/common/config';
import { ConfirmationDialogType } from '@ghostfolio/common/enums';
import {
getDateFnsLocale,
getDateFormatString,

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

@ -1,5 +1,3 @@
import { GfDialogFooterComponent } from '@ghostfolio/client/components/dialog-footer/dialog-footer.component';
import { GfDialogHeaderComponent } from '@ghostfolio/client/components/dialog-header/dialog-header.component';
import { DataService } from '@ghostfolio/client/services/data.service';
import { UserService } from '@ghostfolio/client/services/user/user.service';
import {
@ -22,6 +20,8 @@ import { internalRoutes } from '@ghostfolio/common/routes/routes';
import { GfAccountsTableComponent } from '@ghostfolio/ui/accounts-table';
import { GfActivitiesTableComponent } from '@ghostfolio/ui/activities-table';
import { GfDataProviderCreditsComponent } from '@ghostfolio/ui/data-provider-credits';
import { GfDialogFooterComponent } from '@ghostfolio/ui/dialog-footer';
import { GfDialogHeaderComponent } from '@ghostfolio/ui/dialog-header';
import { GfHistoricalMarketDataEditorComponent } from '@ghostfolio/ui/historical-market-data-editor';
import { translate } from '@ghostfolio/ui/i18n';
import { GfLineChartComponent } from '@ghostfolio/ui/line-chart';

2
apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.component.ts

@ -1,8 +1,8 @@
import { GfDialogHeaderComponent } from '@ghostfolio/client/components/dialog-header/dialog-header.component';
import {
KEY_STAY_SIGNED_IN,
SettingsStorageService
} from '@ghostfolio/client/services/settings-storage.service';
import { GfDialogHeaderComponent } from '@ghostfolio/ui/dialog-header';
import { CommonModule } from '@angular/common';
import { ChangeDetectionStrategy, Component, Inject } from '@angular/core';

2
apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.component.ts

@ -1,7 +1,6 @@
import { NotificationService } from '@ghostfolio/client/core/notification/notification.service';
import { DataService } from '@ghostfolio/client/services/data.service';
import { UserService } from '@ghostfolio/client/services/user/user.service';
import { validateObjectForForm } from '@ghostfolio/client/util/form.util';
import { CreateAccessDto, UpdateAccessDto } from '@ghostfolio/common/dtos';
import {
AssetProfileIdentifier,
@ -9,6 +8,7 @@ import {
PortfolioPosition
} from '@ghostfolio/common/interfaces';
import { AccountWithPlatform } from '@ghostfolio/common/types';
import { validateObjectForForm } from '@ghostfolio/common/utils';
import {
GfPortfolioFilterFormComponent,
PortfolioFilterFormValue

2
apps/client/src/app/components/user-account-access/user-account-access.component.ts

@ -1,10 +1,10 @@
import { GfAccessTableComponent } from '@ghostfolio/client/components/access-table/access-table.component';
import { ConfirmationDialogType } from '@ghostfolio/client/core/notification/confirmation-dialog/confirmation-dialog.type';
import { NotificationService } from '@ghostfolio/client/core/notification/notification.service';
import { DataService } from '@ghostfolio/client/services/data.service';
import { TokenStorageService } from '@ghostfolio/client/services/token-storage.service';
import { UserService } from '@ghostfolio/client/services/user/user.service';
import { CreateAccessDto } from '@ghostfolio/common/dtos';
import { ConfirmationDialogType } from '@ghostfolio/common/enums';
import { Access, User } from '@ghostfolio/common/interfaces';
import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import { GfPremiumIndicatorComponent } from '@ghostfolio/ui/premium-indicator';

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

@ -1,7 +1,7 @@
import { ConfirmationDialogType } from '@ghostfolio/client/core/notification/confirmation-dialog/confirmation-dialog.type';
import { NotificationService } from '@ghostfolio/client/core/notification/notification.service';
import { DataService } from '@ghostfolio/client/services/data.service';
import { UserService } from '@ghostfolio/client/services/user/user.service';
import { ConfirmationDialogType } from '@ghostfolio/common/enums';
import { getDateFormatString } from '@ghostfolio/common/helper';
import { User } from '@ghostfolio/common/interfaces';
import { hasPermission, permissions } from '@ghostfolio/common/permissions';

2
apps/client/src/app/components/user-account-settings/user-account-settings.component.ts

@ -1,4 +1,3 @@
import { ConfirmationDialogType } from '@ghostfolio/client/core/notification/confirmation-dialog/confirmation-dialog.type';
import { NotificationService } from '@ghostfolio/client/core/notification/notification.service';
import { DataService } from '@ghostfolio/client/services/data.service';
import {
@ -9,6 +8,7 @@ import {
import { TokenStorageService } from '@ghostfolio/client/services/token-storage.service';
import { UserService } from '@ghostfolio/client/services/user/user.service';
import { WebAuthnService } from '@ghostfolio/client/services/web-authn.service';
import { ConfirmationDialogType } from '@ghostfolio/common/enums';
import { downloadAsFile } from '@ghostfolio/common/helper';
import { User } from '@ghostfolio/common/interfaces';
import { hasPermission, permissions } from '@ghostfolio/common/permissions';

4
apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts

@ -1,7 +1,7 @@
import { GfDialogFooterComponent } from '@ghostfolio/client/components/dialog-footer/dialog-footer.component';
import { GfDialogHeaderComponent } from '@ghostfolio/client/components/dialog-header/dialog-header.component';
import { AdminService } from '@ghostfolio/client/services/admin.service';
import { AdminUserResponse } from '@ghostfolio/common/interfaces';
import { GfDialogFooterComponent } from '@ghostfolio/ui/dialog-footer';
import { GfDialogHeaderComponent } from '@ghostfolio/ui/dialog-header';
import { GfValueComponent } from '@ghostfolio/ui/value';
import { CommonModule } from '@angular/common';

3
apps/client/src/app/core/notification/confirmation-dialog/confirmation-dialog.component.ts

@ -1,8 +1,9 @@
import { ConfirmationDialogType } from '@ghostfolio/common/enums';
import { Component, HostListener } from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { MatDialogModule, MatDialogRef } from '@angular/material/dialog';
import { ConfirmationDialogType } from './confirmation-dialog.type';
import { ConfirmDialogParams } from './interfaces/interfaces';
@Component({

2
apps/client/src/app/core/notification/confirmation-dialog/interfaces/interfaces.ts

@ -1,4 +1,4 @@
import { ConfirmationDialogType } from '../confirmation-dialog.type';
import { ConfirmationDialogType } from '@ghostfolio/common/enums';
export interface ConfirmDialogParams {
confirmLabel?: string;

2
apps/client/src/app/core/notification/interfaces/interfaces.ts

@ -1,4 +1,4 @@
import { ConfirmationDialogType } from '../confirmation-dialog/confirmation-dialog.type';
import { ConfirmationDialogType } from '@ghostfolio/common/enums';
export interface AlertParams {
discardFn?: () => void;

2
apps/client/src/app/core/notification/notification.service.ts

@ -1,3 +1,4 @@
import { ConfirmationDialogType } from '@ghostfolio/common/enums';
import { translate } from '@ghostfolio/ui/i18n';
import { Injectable } from '@angular/core';
@ -6,7 +7,6 @@ import { isFunction } from 'lodash';
import { GfAlertDialogComponent } from './alert-dialog/alert-dialog.component';
import { GfConfirmationDialogComponent } from './confirmation-dialog/confirmation-dialog.component';
import { ConfirmationDialogType } from './confirmation-dialog/confirmation-dialog.type';
import {
AlertParams,
ConfirmParams,

2
apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.component.ts

@ -1,6 +1,6 @@
import { DataService } from '@ghostfolio/client/services/data.service';
import { validateObjectForForm } from '@ghostfolio/client/util/form.util';
import { CreateAccountDto, UpdateAccountDto } from '@ghostfolio/common/dtos';
import { validateObjectForForm } from '@ghostfolio/common/utils';
import { GfCurrencySelectorComponent } from '@ghostfolio/ui/currency-selector';
import { GfEntityLogoComponent } from '@ghostfolio/ui/entity-logo';

18
apps/client/src/app/pages/blog/2025/11/black-weeks-2025/black-weeks-2025-page.component.ts

@ -0,0 +1,18 @@
import { publicRoutes } from '@ghostfolio/common/routes/routes';
import { GfPremiumIndicatorComponent } from '@ghostfolio/ui/premium-indicator';
import { Component } from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { RouterModule } from '@angular/router';
@Component({
host: { class: 'page' },
imports: [GfPremiumIndicatorComponent, MatButtonModule, RouterModule],
selector: 'gf-black-weeks-2025-page',
templateUrl: './black-weeks-2025-page.html'
})
export class BlackWeeks2025PageComponent {
public routerLinkBlog = publicRoutes.blog.routerLink;
public routerLinkFeatures = publicRoutes.features.routerLink;
public routerLinkPricing = publicRoutes.pricing.routerLink;
}

159
apps/client/src/app/pages/blog/2025/11/black-weeks-2025/black-weeks-2025-page.html

@ -0,0 +1,159 @@
<div class="blog container">
<div class="row">
<div class="col-md-8 offset-md-2">
<article>
<div class="mb-4 text-center">
<h1 class="mb-1">Black Weeks 2025</h1>
<div class="mb-3 text-muted"><small>2025-11-16</small></div>
<img
alt="Black Week 2025 Teaser"
class="rounded w-100"
src="../assets/images/blog/black-weeks-2025.jpg"
title="Black Weeks 2025"
/>
</div>
<section class="mb-4">
<p>
Save <strong>25%</strong> on the
<span class="align-items-center d-inline-flex"
>Ghostfolio Premium
<gf-premium-indicator
class="d-inline-block ml-1"
[enableLink]="false"
/>
</span>
annual plan and get <strong>3 extra months</strong> on top with our
exclusive <strong>Black Weeks</strong> offer.
</p>
</section>
<section class="mb-4">
<p>
<a
href="https://ghostfol.io"
title="Open Source Wealth Management Software"
>Ghostfolio</a
>
unifies your finances in one place and gives you a clear overview of
your portfolio across stocks, ETFs, cryptocurrencies or other
assets. Real time analytics and smart evaluations help you
understand your financial situation quickly and make confident
decisions.
</p>
</section>
<section class="mb-4">
<p>
Grab this limited Black Weeks deal to optimize your financial
future.
</p>
<p class="text-center">
<a color="primary" mat-flat-button [routerLink]="routerLinkPricing"
>Get the offer</a
>
</p>
<p class="mt-5">
More details are available on the
<a [routerLink]="routerLinkPricing">pricing page</a>.
</p>
</section>
<section class="mb-4">
<ul class="list-inline">
<li class="list-inline-item">
<span class="badge badge-light">2025</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Black Friday</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Black Weeks</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Cryptocurrency</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Deal</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Discount</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">ETF</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Finance</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Fintech</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Ghostfolio</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Ghostfolio Premium</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Investment</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Open Source</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">OSS</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Personal Finance</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Portfolio</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Portfolio Tracker</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Pricing</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Promotion</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">SaaS</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Sale</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Savings</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Software</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Stock</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Subscription</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Wealth</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Wealth Management</span>
</li>
</ul>
</section>
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a i18n [routerLink]="routerLinkBlog">Blog</a>
</li>
<li
aria-current="page"
class="active breadcrumb-item text-truncate"
>
Black Weeks 2025
</li>
</ol>
</nav>
</article>
</div>
</div>
</div>

26
apps/client/src/app/pages/blog/blog-page.html

@ -8,6 +8,32 @@
finance</small
>
</h1>
@if (hasPermissionForSubscription) {
<mat-card appearance="outlined" class="mb-3">
<mat-card-content class="p-0">
<div class="container p-0">
<div class="flex-nowrap no-gutters row">
<a
class="d-flex overflow-hidden p-3 w-100"
href="../en/blog/2025/11/black-weeks-2025"
>
<div class="flex-grow-1 overflow-hidden">
<div class="h6 m-0 text-truncate">Black Weeks 2025</div>
<div class="d-flex text-muted">2025-11-15</div>
</div>
<div class="align-items-center d-flex">
<ion-icon
class="chevron text-muted"
name="chevron-forward-outline"
size="small"
/>
</div>
</a>
</div>
</div>
</mat-card-content>
</mat-card>
}
<mat-card appearance="outlined" class="mb-3">
<mat-card-content class="p-0">
<div class="container p-0">

9
apps/client/src/app/pages/blog/blog-page.routes.ts

@ -209,5 +209,14 @@ export const routes: Routes = [
'./2025/09/hacktoberfest-2025/hacktoberfest-2025-page.component'
).then((c) => c.Hacktoberfest2025PageComponent),
title: 'Hacktoberfest 2025'
},
{
canActivate: [AuthGuard],
path: '2025/11/black-weeks-2025',
loadComponent: () =>
import('./2025/11/black-weeks-2025/black-weeks-2025-page.component').then(
(c) => c.BlackWeeks2025PageComponent
),
title: 'Black Weeks 2025'
}
];

2
apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.component.ts

@ -7,6 +7,7 @@ import {
LookupItem
} from '@ghostfolio/common/interfaces';
import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import { validateObjectForForm } from '@ghostfolio/common/utils';
import { GfEntityLogoComponent } from '@ghostfolio/ui/entity-logo';
import { translate } from '@ghostfolio/ui/i18n';
import { GfSymbolAutocompleteComponent } from '@ghostfolio/ui/symbol-autocomplete';
@ -48,7 +49,6 @@ import { EMPTY, Subject } from 'rxjs';
import { catchError, delay, takeUntil } from 'rxjs/operators';
import { DataService } from '../../../../services/data.service';
import { validateObjectForForm } from '../../../../util/form.util';
import { CreateOrUpdateActivityDialogParams } from './interfaces/interfaces';
import { ActivityType } from './types/activity-type.type';

4
apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts

@ -1,5 +1,3 @@
import { GfDialogFooterComponent } from '@ghostfolio/client/components/dialog-footer/dialog-footer.component';
import { GfDialogHeaderComponent } from '@ghostfolio/client/components/dialog-header/dialog-header.component';
import { GfFileDropDirective } from '@ghostfolio/client/directives/file-drop/file-drop.directive';
import { DataService } from '@ghostfolio/client/services/data.service';
import { ImportActivitiesService } from '@ghostfolio/client/services/import-activities.service';
@ -11,6 +9,8 @@ import {
import { Activity, PortfolioPosition } from '@ghostfolio/common/interfaces';
import { GfSymbolPipe } from '@ghostfolio/common/pipes';
import { GfActivitiesTableComponent } from '@ghostfolio/ui/activities-table';
import { GfDialogFooterComponent } from '@ghostfolio/ui/dialog-footer';
import { GfDialogHeaderComponent } from '@ghostfolio/ui/dialog-header';
import {
StepperOrientation,

BIN
apps/client/src/assets/images/blog/black-weeks-2025.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 304 KiB

1
apps/client/src/polyfills.ts

@ -52,3 +52,4 @@ import 'zone.js'; // Included with Angular CLI.
*/
import '@angular/localize/init';
import 'reflect-metadata';

0
apps/client/src/app/core/notification/confirmation-dialog/confirmation-dialog.type.ts → libs/common/src/lib/enums/confirmation-dialog.type.ts

4
libs/common/src/lib/enums/index.ts

@ -0,0 +1,4 @@
import { ConfirmationDialogType } from './confirmation-dialog.type';
import { SubscriptionType } from './subscription-type.type';
export { ConfirmationDialogType, SubscriptionType };

0
libs/common/src/lib/types/subscription-type.type.ts → libs/common/src/lib/enums/subscription-type.type.ts

2
libs/common/src/lib/interfaces/system-message.interface.ts

@ -1,4 +1,4 @@
import { SubscriptionType } from '@ghostfolio/common/types/subscription-type.type';
import { SubscriptionType } from '@ghostfolio/common/enums';
export interface SystemMessage {
message: string;

6
libs/common/src/lib/interfaces/user.interface.ts

@ -1,7 +1,5 @@
import {
AccountWithPlatform,
SubscriptionType
} from '@ghostfolio/common/types';
import { SubscriptionType } from '@ghostfolio/common/enums';
import { AccountWithPlatform } from '@ghostfolio/common/types';
import { Access, Tag } from '@prisma/client';

2
libs/common/src/lib/types/index.ts

@ -18,7 +18,6 @@ import type { Market } from './market.type';
import type { OrderWithAccount } from './order-with-account.type';
import type { RequestWithUser } from './request-with-user.type';
import type { SubscriptionOfferKey } from './subscription-offer-key.type';
import type { SubscriptionType } from './subscription-type.type';
import type { UserWithSettings } from './user-with-settings.type';
import type { ViewMode } from './view-mode.type';
@ -43,7 +42,6 @@ export type {
OrderWithAccount,
RequestWithUser,
SubscriptionOfferKey,
SubscriptionType,
UserWithSettings,
ViewMode
};

2
libs/common/src/lib/types/user-with-settings.type.ts

@ -1,5 +1,5 @@
import { SubscriptionType } from '@ghostfolio/common/enums';
import { SubscriptionOffer, UserSettings } from '@ghostfolio/common/interfaces';
import { SubscriptionType } from '@ghostfolio/common/types';
import { Access, Account, Settings, User } from '@prisma/client';

0
apps/client/src/app/util/form.util.ts → libs/common/src/lib/utils/form.util.ts

3
libs/common/src/lib/utils/index.ts

@ -0,0 +1,3 @@
import { validateObjectForForm } from './form.util';
export { validateObjectForForm };

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

@ -1,10 +1,10 @@
/* eslint-disable @nx/enforce-module-boundaries */
import { ConfirmationDialogType } from '@ghostfolio/client/core/notification/confirmation-dialog/confirmation-dialog.type';
import { NotificationService } from '@ghostfolio/client/core/notification/notification.service';
import { validateObjectForForm } from '@ghostfolio/client/util/form.util';
import { CreateAccountBalanceDto } from '@ghostfolio/common/dtos';
import { ConfirmationDialogType } from '@ghostfolio/common/enums';
import { DATE_FORMAT, getLocale } from '@ghostfolio/common/helper';
import { AccountBalancesResponse } from '@ghostfolio/common/interfaces';
import { validateObjectForForm } from '@ghostfolio/common/utils';
import {
CUSTOM_ELEMENTS_SCHEMA,

2
libs/ui/src/lib/accounts-table/accounts-table.component.ts

@ -1,6 +1,6 @@
/* eslint-disable @nx/enforce-module-boundaries */
import { ConfirmationDialogType } from '@ghostfolio/client/core/notification/confirmation-dialog/confirmation-dialog.type';
import { NotificationService } from '@ghostfolio/client/core/notification/notification.service';
import { ConfirmationDialogType } from '@ghostfolio/common/enums';
import { getLocale } from '@ghostfolio/common/helper';
import { GfEntityLogoComponent } from '@ghostfolio/ui/entity-logo';
import { GfValueComponent } from '@ghostfolio/ui/value';

2
libs/ui/src/lib/activities-table/activities-table.component.ts

@ -1,10 +1,10 @@
/* eslint-disable @nx/enforce-module-boundaries */
import { ConfirmationDialogType } from '@ghostfolio/client/core/notification/confirmation-dialog/confirmation-dialog.type';
import { NotificationService } from '@ghostfolio/client/core/notification/notification.service';
import {
DEFAULT_PAGE_SIZE,
TAG_ID_EXCLUDE_FROM_ANALYSIS
} from '@ghostfolio/common/config';
import { ConfirmationDialogType } from '@ghostfolio/common/enums';
import { getLocale } from '@ghostfolio/common/helper';
import {
Activity,

4
libs/ui/src/lib/benchmark/benchmark-detail-dialog/benchmark-detail-dialog.component.ts

@ -1,12 +1,12 @@
/* eslint-disable @nx/enforce-module-boundaries */
import { GfDialogFooterComponent } from '@ghostfolio/client/components/dialog-footer/dialog-footer.component';
import { GfDialogHeaderComponent } from '@ghostfolio/client/components/dialog-header/dialog-header.component';
import { DataService } from '@ghostfolio/client/services/data.service';
import { DATE_FORMAT } from '@ghostfolio/common/helper';
import {
AdminMarketDataDetails,
LineChartItem
} from '@ghostfolio/common/interfaces';
import { GfDialogFooterComponent } from '@ghostfolio/ui/dialog-footer';
import { GfDialogHeaderComponent } from '@ghostfolio/ui/dialog-header';
import {
CUSTOM_ELEMENTS_SCHEMA,

2
libs/ui/src/lib/benchmark/benchmark.component.ts

@ -1,6 +1,6 @@
/* eslint-disable @nx/enforce-module-boundaries */
import { ConfirmationDialogType } from '@ghostfolio/client/core/notification/confirmation-dialog/confirmation-dialog.type';
import { NotificationService } from '@ghostfolio/client/core/notification/notification.service';
import { ConfirmationDialogType } from '@ghostfolio/common/enums';
import { getLocale, resolveMarketCondition } from '@ghostfolio/common/helper';
import {
AssetProfileIdentifier,

0
apps/client/src/app/components/dialog-footer/dialog-footer.component.html → libs/ui/src/lib/dialog-footer/dialog-footer.component.html

0
apps/client/src/app/components/dialog-footer/dialog-footer.component.scss → libs/ui/src/lib/dialog-footer/dialog-footer.component.scss

0
apps/client/src/app/components/dialog-footer/dialog-footer.component.ts → libs/ui/src/lib/dialog-footer/dialog-footer.component.ts

1
libs/ui/src/lib/dialog-footer/index.ts

@ -0,0 +1 @@
export * from './dialog-footer.component';

0
apps/client/src/app/components/dialog-header/dialog-header.component.html → libs/ui/src/lib/dialog-header/dialog-header.component.html

0
apps/client/src/app/components/dialog-header/dialog-header.component.scss → libs/ui/src/lib/dialog-header/dialog-header.component.scss

0
apps/client/src/app/components/dialog-header/dialog-header.component.ts → libs/ui/src/lib/dialog-header/dialog-header.component.ts

1
libs/ui/src/lib/dialog-header/index.ts

@ -0,0 +1 @@
export * from './dialog-header.component';

4
package-lock.json

@ -1,12 +1,12 @@
{
"name": "ghostfolio",
"version": "2.216.0",
"version": "2.217.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "ghostfolio",
"version": "2.216.0",
"version": "2.217.1",
"hasInstallScript": true,
"license": "AGPL-3.0",
"dependencies": {

2
package.json

@ -1,6 +1,6 @@
{
"name": "ghostfolio",
"version": "2.216.0",
"version": "2.217.1",
"homepage": "https://ghostfol.io",
"license": "AGPL-3.0",
"repository": "https://github.com/ghostfolio/ghostfolio",

Loading…
Cancel
Save