From 61ef2631f9d57e30fda2526f4a3afcab10d77abf Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Sun, 21 Dec 2025 16:39:42 +0100 Subject: [PATCH 1/5] Feature/extend personal finance tools 20251220 (#6087) * Add BlueBudget, Moneyspire and Pennies --- libs/common/src/lib/personal-finance-tools.ts | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/libs/common/src/lib/personal-finance-tools.ts b/libs/common/src/lib/personal-finance-tools.ts index 7d1c4434a..6d0a85fb2 100644 --- a/libs/common/src/lib/personal-finance-tools.ts +++ b/libs/common/src/lib/personal-finance-tools.ts @@ -107,6 +107,15 @@ export const personalFinanceTools: Product[] = [ pricingPerYear: '$100', slogan: 'Stock Portfolio Tracker for Smart Investors' }, + { + founded: 2024, + hasSelfHostingAbility: false, + key: 'bluebudget', + languages: ['Deutsch', 'English', 'Français', 'Italiano'], + name: 'BlueBudget', + origin: 'Switzerland', + slogan: 'Schweizer Budget App für einfache & smarte Budgetplanung' + }, { key: 'budgetpulse', name: 'BudgetPulse', @@ -641,6 +650,15 @@ export const personalFinanceTools: Product[] = [ origin: 'Germany', slogan: 'Dein smarter Finance Assistant' }, + { + founded: 2007, + key: 'moneyspire', + name: 'Moneyspire', + note: 'License is a perpetual license', + origin: 'United States', + pricingPerYear: '$59.99', + slogan: 'Have total control of your financial life' + }, { key: 'moneywiz', name: 'MoneyWiz', @@ -716,6 +734,13 @@ export const personalFinanceTools: Product[] = [ origin: 'Singapore', slogan: 'Feel in control of your money without spreadsheets or shame' }, + { + key: 'pennies', + name: 'Pennies', + origin: 'United States', + pricingPerYear: '$39.99', + slogan: 'Your money. Made simple.' + }, { founded: 2022, hasFreePlan: true, From 1a2ca71ef8d29d21f6e48c7094fb3a4c66cc6c39 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Sun, 21 Dec 2025 16:49:44 +0100 Subject: [PATCH 2/5] Task/refactor href in user account settings component (#6085) * Refactoring --- .../user-account-settings/user-account-settings.component.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/client/src/app/components/user-account-settings/user-account-settings.component.ts b/apps/client/src/app/components/user-account-settings/user-account-settings.component.ts index e17425676..e0028bb5c 100644 --- a/apps/client/src/app/components/user-account-settings/user-account-settings.component.ts +++ b/apps/client/src/app/components/user-account-settings/user-account-settings.component.ts @@ -11,6 +11,7 @@ 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'; +import { internalRoutes } from '@ghostfolio/common/routes/routes'; import { NotificationService } from '@ghostfolio/ui/notifications'; import { @@ -169,9 +170,9 @@ export class GfUserAccountSettingsComponent implements OnDestroy, OnInit { if (aKey === 'language') { if (aValue) { - window.location.href = `../${aValue}/account`; + window.location.href = `../${aValue}/${internalRoutes.account.path}`; } else { - window.location.href = `../`; + window.location.href = '../'; } } }); From 00f943bb3b803a28968032e385d3e86cf5a95ffc Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Mon, 22 Dec 2025 12:30:51 +0100 Subject: [PATCH 3/5] Task/upgrade shx to version 0.4.0 (#6088) * Upgrade shx to version 0.4.0 * Update changelog --- CHANGELOG.md | 6 ++ package-lock.json | 193 +++++++++++++++++++++++++++++++++++++++------- package.json | 2 +- 3 files changed, 173 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0bdfb7eb4..c96ce0a50 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ 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 + +### Changed + +- Upgraded `shx` from version `0.3.4` to `0.4.0` + ## 2.224.2 - 2025-12-20 ### Added diff --git a/package-lock.json b/package-lock.json index 986815b83..1cee0fa88 100644 --- a/package-lock.json +++ b/package-lock.json @@ -148,7 +148,7 @@ "react": "18.2.0", "react-dom": "18.2.0", "replace-in-file": "8.3.0", - "shx": "0.3.4", + "shx": "0.4.0", "storybook": "9.1.5", "ts-jest": "29.4.0", "ts-node": "10.9.2", @@ -32982,6 +32982,13 @@ "@stripe/stripe-js": ">=7.0.0 <8.0.0" } }, + "node_modules/nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true, + "license": "MIT" + }, "node_modules/no-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", @@ -34294,6 +34301,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -36030,8 +36047,6 @@ "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", "dev": true, "license": "MIT", - "optional": true, - "peer": true, "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" @@ -38033,60 +38048,174 @@ } }, "node_modules/shelljs": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", - "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.9.2.tgz", + "integrity": "sha512-S3I64fEiKgTZzKCC46zT/Ib9meqofLrQVbpSswtjFfAVDW+AZ54WTnAM/3/yENoxz/V1Cy6u3kiiEbQ4DNphvw==", "dev": true, "license": "BSD-3-Clause", "dependencies": { - "glob": "^7.0.0", + "execa": "^1.0.0", + "fast-glob": "^3.3.2", "interpret": "^1.0.0", "rechoir": "^0.6.2" }, "bin": { "shjs": "bin/shjs" }, + "engines": { + "node": ">=18" + } + }, + "node_modules/shelljs/node_modules/cross-spawn": { + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", + "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "engines": { + "node": ">=4.8" + } + }, + "node_modules/shelljs/node_modules/execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/shelljs/node_modules/get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/shelljs/node_modules/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/shelljs/node_modules/npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^2.0.0" + }, "engines": { "node": ">=4" } }, - "node_modules/shelljs/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", + "node_modules/shelljs/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/shelljs/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/shelljs/node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "dev": true, + "license": "MIT", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "shebang-regex": "^1.0.0" }, "engines": { - "node": "*" + "node": ">=0.10.0" + } + }, + "node_modules/shelljs/node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/shelljs/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/shelljs/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "bin": { + "which": "bin/which" } }, "node_modules/shx": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/shx/-/shx-0.3.4.tgz", - "integrity": "sha512-N6A9MLVqjxZYcVn8hLmtneQWIJtp8IKzMP4eMnx+nqkvXoqinUPCbUFLp2UcWTEIUONhlk0ewxr/jaVGlc+J+g==", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/shx/-/shx-0.4.0.tgz", + "integrity": "sha512-Z0KixSIlGPpijKgcH6oCMCbltPImvaKy0sGH8AkLRXw1KyzpKtaCTizP2xen+hNDqVF4xxgvA0KXSb9o4Q6hnA==", "dev": true, "license": "MIT", "dependencies": { - "minimist": "^1.2.3", - "shelljs": "^0.8.5" + "minimist": "^1.2.8", + "shelljs": "^0.9.2" }, "bin": { "shx": "lib/cli.js" }, "engines": { - "node": ">=6" + "node": ">=18" } }, "node_modules/side-channel": { @@ -38910,6 +39039,16 @@ "node": ">=8" } }, + "node_modules/strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/strip-final-newline": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", diff --git a/package.json b/package.json index 1ddb69001..042045528 100644 --- a/package.json +++ b/package.json @@ -192,7 +192,7 @@ "react": "18.2.0", "react-dom": "18.2.0", "replace-in-file": "8.3.0", - "shx": "0.3.4", + "shx": "0.4.0", "storybook": "9.1.5", "ts-jest": "29.4.0", "ts-node": "10.9.2", From 5727ce33751fe4acebcbdfa2a6dbf37728093fe5 Mon Sep 17 00:00:00 2001 From: Karel De Smet Date: Tue, 23 Dec 2025 17:21:01 +0100 Subject: [PATCH 4/5] Bugfix/add missing currency suffix to cash balance field in create or update account dialog (#6092) * Add missing currency suffix to cash balance field in create or update account dialog * Update changelog --- CHANGELOG.md | 4 ++++ .../create-or-update-account-dialog.html | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c96ce0a50..cef903f16 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Upgraded `shx` from version `0.3.4` to `0.4.0` +### Fixed + +- Added the missing currency suffix to the cash balance field in the create or update account dialog + ## 2.224.2 - 2025-12-20 ### Added diff --git a/apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html b/apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html index 9a9d89624..7340e017d 100644 --- a/apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html +++ b/apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html @@ -39,7 +39,7 @@ (keydown.enter)="$event.stopPropagation()" /> {{ - accountForm.get('currency')?.value?.value + accountForm.get('currency')?.value }} From 7b885a11e23149ad942b8b147e0a09785d16297d Mon Sep 17 00:00:00 2001 From: Eduardo Almeida Date: Wed, 24 Dec 2025 10:18:13 +0000 Subject: [PATCH 5/5] Bugfix/fix asset profile deletion and allow editing asset profile identifiers with MANUAL data source (#6090) * Fix asset profile deletion and allow editing asset profile identifiers with MANUAL data source * Update changelog --- CHANGELOG.md | 2 ++ .../services/symbol-profile/symbol-profile.service.ts | 10 ++++++---- .../asset-profile-dialog.component.ts | 7 ------- .../asset-profile-dialog/asset-profile-dialog.html | 6 ++---- .../interfaces/enhanced-symbol-profile.interface.ts | 1 + 5 files changed, 11 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cef903f16..789dd28af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,11 +9,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Lifted the asset profile identifier editing restriction for `MANUAL` data sources in the asset profile details dialog of the admin control panel - Upgraded `shx` from version `0.3.4` to `0.4.0` ### Fixed - Added the missing currency suffix to the cash balance field in the create or update account dialog +- Fixed the delete button in the asset profile details dialog of the admin control panel by providing the missing `watchedByCount` parameter ## 2.224.2 - 2025-12-20 diff --git a/apps/api/src/services/symbol-profile/symbol-profile.service.ts b/apps/api/src/services/symbol-profile/symbol-profile.service.ts index c41a59c78..4c2c42589 100644 --- a/apps/api/src/services/symbol-profile/symbol-profile.service.ts +++ b/apps/api/src/services/symbol-profile/symbol-profile.service.ts @@ -77,7 +77,7 @@ export class SymbolProfileService { .findMany({ include: { _count: { - select: { activities: true } + select: { activities: true, watchedBy: true } }, activities: { orderBy: { @@ -109,7 +109,7 @@ export class SymbolProfileService { .findMany({ include: { _count: { - select: { activities: true } + select: { activities: true, watchedBy: true } }, SymbolProfileOverrides: true }, @@ -184,7 +184,7 @@ export class SymbolProfileService { private enhanceSymbolProfiles( symbolProfiles: (SymbolProfile & { - _count: { activities: number }; + _count: { activities: number; watchedBy?: number }; activities?: { date: Date; }[]; @@ -206,10 +206,12 @@ export class SymbolProfileService { sectors: this.getSectors( symbolProfile?.sectors as unknown as Prisma.JsonArray ), - symbolMapping: this.getSymbolMapping(symbolProfile) + symbolMapping: this.getSymbolMapping(symbolProfile), + watchedByCount: 0 }; item.activitiesCount = symbolProfile._count.activities; + item.watchedByCount = symbolProfile._count.watchedBy ?? 0; delete item._count; item.dateOfFirstActivity = symbolProfile.activities?.[0]?.date; diff --git a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts index 3fe944a25..57ee57f19 100644 --- a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts +++ b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts @@ -260,13 +260,6 @@ export class GfAssetProfileDialogComponent implements OnDestroy, OnInit { addIcons({ createOutline, ellipsisVertical, readerOutline, serverOutline }); } - public get canEditAssetProfileIdentifier() { - return ( - this.assetProfile?.assetClass && - !['MANUAL'].includes(this.assetProfile?.dataSource) - ); - } - public get canSaveAssetProfileIdentifier() { return !this.assetProfileForm.dirty; } diff --git a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html index 5f684ab47..ce0cafbc1 100644 --- a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html +++ b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html @@ -73,7 +73,8 @@ !adminMarketDataService.hasPermissionToDeleteAssetProfile({ activitiesCount: assetProfile?.activitiesCount, isBenchmark: isBenchmark, - symbol: data.symbol + symbol: data.symbol, + watchedByCount: assetProfile?.watchedByCount }) " (click)=" @@ -186,9 +187,6 @@ mat-button type="button" [disabled]="!canSaveAssetProfileIdentifier" - [ngClass]="{ - 'd-none': !canEditAssetProfileIdentifier - }" (click)="onSetEditAssetProfileIdentifierMode()" > diff --git a/libs/common/src/lib/interfaces/enhanced-symbol-profile.interface.ts b/libs/common/src/lib/interfaces/enhanced-symbol-profile.interface.ts index 4cc6ba8aa..8426916c9 100644 --- a/libs/common/src/lib/interfaces/enhanced-symbol-profile.interface.ts +++ b/libs/common/src/lib/interfaces/enhanced-symbol-profile.interface.ts @@ -33,4 +33,5 @@ export interface EnhancedSymbolProfile { updatedAt: Date; url?: string; userId?: string; + watchedByCount?: number; }