From c3596ea292b5621a3f448e5f5ac86fab1b2cfdc3 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Fri, 16 Oct 2026 14:32:32 +0200 Subject: [PATCH 01/10] Task/move investment chart component to UI library and add Storybook story (#8192) * Move investment chart component to UI library * Add Storybook story for investment chart component * Update changelog --- CHANGELOG.md | 5 ++ .../account-detail-dialog.component.ts | 2 +- .../analysis/analysis-page.component.ts | 2 +- libs/ui/src/lib/investment-chart/index.ts | 1 + .../investment-chart.component.html | 0 .../investment-chart.component.scss | 0 .../investment-chart.component.stories.ts | 75 +++++++++++++++++++ .../investment-chart.component.ts | 11 +-- 8 files changed, 89 insertions(+), 7 deletions(-) create mode 100644 libs/ui/src/lib/investment-chart/index.ts rename {apps/client/src/app/components => libs/ui/src/lib}/investment-chart/investment-chart.component.html (100%) rename {apps/client/src/app/components => libs/ui/src/lib}/investment-chart/investment-chart.component.scss (100%) create mode 100644 libs/ui/src/lib/investment-chart/investment-chart.component.stories.ts rename {apps/client/src/app/components => libs/ui/src/lib}/investment-chart/investment-chart.component.ts (99%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 289c696e31..278afc1823 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,8 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Added + +- Added a _Storybook_ story for the investment chart component + ### Changed +- Moved the investment chart component to `@ghostfolio/ui` - Hardened the validation of the date in the endpoint to create an account balance - Upgraded `bootstrap` from version `4.6.2` to `5.3.8` - Upgraded `stripe` from version `22.6.2` to `23.0.0` diff --git a/apps/client/src/app/components/account-detail-dialog/account-detail-dialog.component.ts b/apps/client/src/app/components/account-detail-dialog/account-detail-dialog.component.ts index 9a1dfe9438..f6fe52a8bd 100644 --- a/apps/client/src/app/components/account-detail-dialog/account-detail-dialog.component.ts +++ b/apps/client/src/app/components/account-detail-dialog/account-detail-dialog.component.ts @@ -1,4 +1,3 @@ -import { GfInvestmentChartComponent } from '@ghostfolio/client/components/investment-chart/investment-chart.component'; import { UserService } from '@ghostfolio/client/services/user/user.service'; import { DEFAULT_DATE_RANGE, @@ -23,6 +22,7 @@ import { GfDialogFooterComponent } from '@ghostfolio/ui/dialog-footer'; import { GfDialogHeaderComponent } from '@ghostfolio/ui/dialog-header'; import { GfHoldingsTableComponent } from '@ghostfolio/ui/holdings-table'; import { translate } from '@ghostfolio/ui/i18n'; +import { GfInvestmentChartComponent } from '@ghostfolio/ui/investment-chart'; import { DataService } from '@ghostfolio/ui/services'; import { GfTagsSelectorComponent } from '@ghostfolio/ui/tags-selector'; import { GfValueComponent } from '@ghostfolio/ui/value'; diff --git a/apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts b/apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts index 26e52678c7..8ba3f0fd8a 100644 --- a/apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts +++ b/apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts @@ -1,5 +1,4 @@ import { GfBenchmarkComparatorComponent } from '@ghostfolio/client/components/benchmark-comparator/benchmark-comparator.component'; -import { GfInvestmentChartComponent } from '@ghostfolio/client/components/investment-chart/investment-chart.component'; import { UserService } from '@ghostfolio/client/services/user/user.service'; import { DEFAULT_DATE_RANGE, @@ -25,6 +24,7 @@ import type { import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; import type { SymbolProfile } from '@ghostfolio/prisma/browser'; import { translate } from '@ghostfolio/ui/i18n'; +import { GfInvestmentChartComponent } from '@ghostfolio/ui/investment-chart'; import { GfPremiumIndicatorComponent } from '@ghostfolio/ui/premium-indicator'; import { DataService } from '@ghostfolio/ui/services'; import { GfToggleComponent } from '@ghostfolio/ui/toggle'; diff --git a/libs/ui/src/lib/investment-chart/index.ts b/libs/ui/src/lib/investment-chart/index.ts new file mode 100644 index 0000000000..3929e41867 --- /dev/null +++ b/libs/ui/src/lib/investment-chart/index.ts @@ -0,0 +1 @@ +export * from './investment-chart.component'; diff --git a/apps/client/src/app/components/investment-chart/investment-chart.component.html b/libs/ui/src/lib/investment-chart/investment-chart.component.html similarity index 100% rename from apps/client/src/app/components/investment-chart/investment-chart.component.html rename to libs/ui/src/lib/investment-chart/investment-chart.component.html diff --git a/apps/client/src/app/components/investment-chart/investment-chart.component.scss b/libs/ui/src/lib/investment-chart/investment-chart.component.scss similarity index 100% rename from apps/client/src/app/components/investment-chart/investment-chart.component.scss rename to libs/ui/src/lib/investment-chart/investment-chart.component.scss diff --git a/libs/ui/src/lib/investment-chart/investment-chart.component.stories.ts b/libs/ui/src/lib/investment-chart/investment-chart.component.stories.ts new file mode 100644 index 0000000000..225857c7da --- /dev/null +++ b/libs/ui/src/lib/investment-chart/investment-chart.component.stories.ts @@ -0,0 +1,75 @@ +import { moduleMetadata } from '@storybook/angular'; +import type { Meta, StoryObj } from '@storybook/angular'; +import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; + +import { GfInvestmentChartComponent } from './investment-chart.component'; + +export default { + title: 'Investment Chart', + component: GfInvestmentChartComponent, + decorators: [ + moduleMetadata({ + imports: [NgxSkeletonLoaderModule] + }) + ] +} as Meta; + +type Story = StoryObj; + +export const PerformanceByYear: Story = { + args: { + benchmarkDataItems: [ + { + date: '2015-01-01', + investment: -0.02783300898051616 + }, + { + date: '2016-01-01', + investment: 0.2955008663975502 + }, + { + date: '2017-01-01', + investment: 0.5581973009289353 + }, + { + date: '2018-01-01', + investment: -0.27056776062551113 + }, + { + date: '2019-01-01', + investment: 0.27802802888773426 + }, + { + date: '2020-01-01', + investment: 0.11264960377555408 + }, + { + date: '2021-01-01', + investment: 0.31365756351078433 + }, + { + date: '2022-01-01', + investment: -0.23865494728984496 + }, + { + date: '2023-01-01', + investment: 0.07752911954819477 + }, + { + date: '2024-01-01', + investment: 0.24854314905668837 + }, + { + date: '2025-01-01', + investment: 0.05351048565996891 + }, + { + date: '2026-01-01', + investment: 0.1037188655512382 + } + ], + benchmarkDataLabel: 'Net Performance', + groupBy: 'year', + isInPercentage: true + } +}; diff --git a/apps/client/src/app/components/investment-chart/investment-chart.component.ts b/libs/ui/src/lib/investment-chart/investment-chart.component.ts similarity index 99% rename from apps/client/src/app/components/investment-chart/investment-chart.component.ts rename to libs/ui/src/lib/investment-chart/investment-chart.component.ts index 8b74a74d4c..86af956d45 100644 --- a/apps/client/src/app/components/investment-chart/investment-chart.component.ts +++ b/libs/ui/src/lib/investment-chart/investment-chart.component.ts @@ -12,11 +12,6 @@ import { getLocale, parseDate } from '@ghostfolio/common/helper'; import { LineChartItem } from '@ghostfolio/common/interfaces'; import { InvestmentItem } from '@ghostfolio/common/interfaces/investment-item.interface'; import { ColorScheme, GroupBy } from '@ghostfolio/common/types'; -import { - getTimeSeriesTooltipOptions, - onPrefersColorSchemeChange, - registerChartConfiguration -} from '@ghostfolio/ui/chart'; import { ChangeDetectionStrategy, @@ -47,6 +42,12 @@ import { type AnnotationOptions } from 'chartjs-plugin-annotation'; import { isFuture } from 'date-fns'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; +import { + getTimeSeriesTooltipOptions, + onPrefersColorSchemeChange, + registerChartConfiguration +} from '../chart'; + @Component({ changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgxSkeletonLoaderModule], From 5bb623c529b235a3c0b6dd54d138a84f52b1c8d8 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Fri, 16 Oct 2026 17:23:01 +0200 Subject: [PATCH 02/10] Bugfix/add missing user id to unique key of account balance (#8179) * Add missing userId to unique constraint of account balance * Update changelog --- CHANGELOG.md | 1 + apps/api/src/app/account-balance/account-balance.service.ts | 4 ++-- .../migration.sql | 5 +++++ prisma/schema.prisma | 2 +- 4 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 prisma/migrations/20261014000000_added_user_id_to_unique_constraint_of_account_balance/migration.sql diff --git a/CHANGELOG.md b/CHANGELOG.md index 278afc1823..591da356cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Fixed an exception in the authentication interceptor when the time zone of the browser is not available +- Fixed the missing `userId` in the unique constraint of the `AccountBalance` database schema ## 3.86.0 - 2026-10-13 diff --git a/apps/api/src/app/account-balance/account-balance.service.ts b/apps/api/src/app/account-balance/account-balance.service.ts index 56975cdf08..afa4386e01 100644 --- a/apps/api/src/app/account-balance/account-balance.service.ts +++ b/apps/api/src/app/account-balance/account-balance.service.ts @@ -68,9 +68,9 @@ export class AccountBalanceService { value: balance }, where: { - userId, - accountId_date: { + accountId_userId_date: { accountId, + userId, date: resetHours(parseISO(date)) } } diff --git a/prisma/migrations/20261014000000_added_user_id_to_unique_constraint_of_account_balance/migration.sql b/prisma/migrations/20261014000000_added_user_id_to_unique_constraint_of_account_balance/migration.sql new file mode 100644 index 0000000000..40c76e5816 --- /dev/null +++ b/prisma/migrations/20261014000000_added_user_id_to_unique_constraint_of_account_balance/migration.sql @@ -0,0 +1,5 @@ +-- DropIndex +DROP INDEX "AccountBalance_accountId_date_key"; + +-- CreateIndex +CREATE UNIQUE INDEX "AccountBalance_accountId_userId_date_key" ON "AccountBalance"("accountId", "userId", "date"); diff --git a/prisma/schema.prisma b/prisma/schema.prisma index e21fc10e8c..17a2d4e39d 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -65,7 +65,7 @@ model AccountBalance { userId String value Float - @@unique([accountId, date]) + @@unique([accountId, userId, date]) @@index([accountId]) @@index([date]) } From 2869eed07a321cf92f72ee2d03199dc950b51d34 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Sat, 17 Oct 2026 09:33:55 +0200 Subject: [PATCH 03/10] Bugfix/visibility of Try Premium button (#8187) Fix visibility of Try Premium button --- .../user-account-membership.html | 2 +- apps/client/src/app/services/user/user.service.ts | 10 ++++++++++ libs/common/src/lib/interfaces/user.interface.ts | 1 + 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/apps/client/src/app/components/user-account-membership/user-account-membership.html b/apps/client/src/app/components/user-account-membership/user-account-membership.html index ae23dc9860..f18da13ed9 100644 --- a/apps/client/src/app/components/user-account-membership/user-account-membership.html +++ b/apps/client/src/app/components/user-account-membership/user-account-membership.html @@ -50,7 +50,7 @@ } }
- @if (!user?.subscription?.expiresAt) { + @if (!user?.subscription?.subscribedAt) { { user.settings.retirementDate = parseISO(user.settings.retirementDate); } + if (user.subscription?.expiresAt) { + user.subscription.expiresAt = parseISO(user.subscription.expiresAt); + } + + if (user.subscription?.subscribedAt) { + user.subscription.subscribedAt = parseISO( + user.subscription.subscribedAt + ); + } + this.setState({ user }, UserStoreActions.GetUser); if ( diff --git a/libs/common/src/lib/interfaces/user.interface.ts b/libs/common/src/lib/interfaces/user.interface.ts index ff0f6646f5..aa079c1219 100644 --- a/libs/common/src/lib/interfaces/user.interface.ts +++ b/libs/common/src/lib/interfaces/user.interface.ts @@ -27,6 +27,7 @@ export interface User { subscription: { expiresAt?: Date; offer: SubscriptionOffer; + subscribedAt?: Date; type: SubscriptionType; }; tags: (Tag & { isUsed: boolean })[]; From d214aa10bde08c9319cfcd1b09e95d48c066b3a4 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Sat, 17 Oct 2026 09:35:32 +0200 Subject: [PATCH 04/10] Task/remove unused warn color- #8199 (#8199) Remove unused warn color --- libs/common/src/lib/config.ts | 7 ------- 1 file changed, 7 deletions(-) diff --git a/libs/common/src/lib/config.ts b/libs/common/src/lib/config.ts index 48f69fd403..615ed09214 100644 --- a/libs/common/src/lib/config.ts +++ b/libs/common/src/lib/config.ts @@ -31,13 +31,6 @@ export const secondaryColorRgb = { b: 207 }; -export const warnColorHex = '#dc3545'; -export const warnColorRgb = { - r: 220, - g: 53, - b: 69 -}; - export const AI_ACTIVITIES_FILE_MAXIMUM_SIZE = 10 * 1024 * 1024; export const AI_ACTIVITIES_TIMEOUT = ms('2 minutes'); export const AI_MAX_ACTIVITIES = 25; From 463128175895a7b6f6ed734aec58c07065e7c04c Mon Sep 17 00:00:00 2001 From: Siddheya Kulkarni <115717746+Asymtode712@users.noreply.github.com> Date: Sat, 17 Oct 2026 13:14:03 +0530 Subject: [PATCH 05/10] Task/replace custom line height class with Bootstrap utility (#8194) * Replaced custom line height class * Replace line-height-normal with lh-normal --- .../admin-market-data/admin-market-data.html | 12 +++----- .../admin-settings.component.html | 2 +- .../components/footer/footer.component.html | 2 +- .../src/app/components/markets/markets.html | 2 +- .../about/oss-friends/oss-friends-page.html | 2 +- apps/client/src/app/pages/blog/blog-page.html | 2 +- .../src/app/pages/features/features-page.html | 2 +- .../create-or-update-activity-dialog.html | 28 +++++-------------- .../import-activities-dialog.html | 1 - .../src/app/pages/pricing/pricing-page.html | 6 ++-- apps/client/src/styles.scss | 8 ------ apps/client/src/styles/bootstrap.scss | 11 +++++++- .../account-selector.component.html | 4 +-- .../activities-table.component.html | 2 +- .../assistant-list-item.html | 2 +- .../lib/benchmark/benchmark.component.html | 6 +--- .../currency-selector.component.html | 2 +- .../entity-logo/entity-logo.component.html | 2 +- .../holdings-table.component.html | 2 +- .../lib/page-tabs/page-tabs.component.html | 2 +- .../portfolio-filter-form.component.html | 2 +- .../symbol-autocomplete.component.html | 5 ++-- .../top-holdings/top-holdings.component.html | 2 +- 23 files changed, 42 insertions(+), 67 deletions(-) diff --git a/apps/client/src/app/components/admin-market-data/admin-market-data.html b/apps/client/src/app/components/admin-market-data/admin-market-data.html index 32dc2cdfbe..16447fc1f4 100644 --- a/apps/client/src/app/components/admin-market-data/admin-market-data.html +++ b/apps/client/src/app/components/admin-market-data/admin-market-data.html @@ -87,11 +87,7 @@ > Name - +
{{ element.name }}
@if (!isUUID(element.symbol)) {
@@ -110,7 +106,7 @@
@@ -132,7 +128,7 @@
{{ translate(element.assetClass) }}
@@ -152,7 +148,7 @@
{{ translate(element.assetSubClass) }}
diff --git a/apps/client/src/app/components/admin-settings/admin-settings.component.html b/apps/client/src/app/components/admin-settings/admin-settings.component.html index 32cd9eaf4c..a991b8a0f3 100644 --- a/apps/client/src/app/components/admin-settings/admin-settings.component.html +++ b/apps/client/src/app/components/admin-settings/admin-settings.component.html @@ -90,7 +90,7 @@ }
@if (isGhostfolioApiKeyValid === true) { -
+
Valid until {{ diff --git a/apps/client/src/app/components/footer/footer.component.html b/apps/client/src/app/components/footer/footer.component.html index dbaaab11a4..143fe27a7a 100644 --- a/apps/client/src/app/components/footer/footer.component.html +++ b/apps/client/src/app/components/footer/footer.component.html @@ -192,6 +192,6 @@
-
Ghostfolio
+
Ghostfolio
diff --git a/apps/client/src/app/components/markets/markets.html b/apps/client/src/app/components/markets/markets.html index eeb6ce5791..73e27e181e 100644 --- a/apps/client/src/app/components/markets/markets.html +++ b/apps/client/src/app/components/markets/markets.html @@ -58,7 +58,7 @@ /> @if (benchmarks()?.length) {
Calculations are based on delayed market data and may not be diff --git a/apps/client/src/app/pages/about/oss-friends/oss-friends-page.html b/apps/client/src/app/pages/about/oss-friends/oss-friends-page.html index cf8bb9b804..b53b5096d1 100644 --- a/apps/client/src/app/pages/about/oss-friends/oss-friends-page.html +++ b/apps/client/src/app/pages/about/oss-friends/oss-friends-page.html @@ -1,7 +1,7 @@
-

+

Our OSS Friends diff --git a/apps/client/src/app/pages/blog/blog-page.html b/apps/client/src/app/pages/blog/blog-page.html index 3c1aacddc5..3a09b108d4 100644 --- a/apps/client/src/app/pages/blog/blog-page.html +++ b/apps/client/src/app/pages/blog/blog-page.html @@ -1,7 +1,7 @@
-

+

Blog Discover the latest Ghostfolio updates and insights on personal diff --git a/apps/client/src/app/pages/features/features-page.html b/apps/client/src/app/pages/features/features-page.html index b57ecba2e4..2c3e25d0da 100644 --- a/apps/client/src/app/pages/features/features-page.html +++ b/apps/client/src/app/pages/features/features-page.html @@ -1,7 +1,7 @@
-

+

Features Check out the numerous features of Ghostfolio to manage your wealth diff --git a/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.html b/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.html index 693e63dad2..608b1d9084 100644 --- a/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.html +++ b/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.html @@ -21,9 +21,7 @@ {{ typesTranslationMap['BUY'] }} - Stocks, ETFs, bonds, cryptocurrencies, commodities @@ -31,9 +29,7 @@ {{ typesTranslationMap['FEE'] }} - One-time fee, annual account fees @@ -41,9 +37,7 @@ {{ typesTranslationMap['DIVIDEND'] }} - Distribution of corporate earnings @@ -51,9 +45,7 @@ {{ typesTranslationMap['INTEREST'] }} - Revenue for lending out money @@ -61,9 +53,7 @@ {{ typesTranslationMap['LIABILITY'] }} - Mortgages, personal loans, credit cards @@ -71,9 +61,7 @@ {{ typesTranslationMap['SELL'] }} - Stocks, ETFs, bonds, cryptocurrencies, commodities @@ -81,9 +69,7 @@ {{ typesTranslationMap['VALUABLE'] }} - Luxury items, real estate, private companies diff --git a/apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.html b/apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.html index ae67e5a97e..c68594c7b5 100644 --- a/apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.html +++ b/apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.html @@ -38,7 +38,6 @@ }} @for (holding of holdings; track holding) { -
  • +