diff --git a/CHANGELOG.md b/CHANGELOG.md
index 758348d68..d288d79ba 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,18 @@ 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
+
+- 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
### Added
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/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 = '../';
}
}
});
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
}}
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;
}
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,
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",