From 138d867e8dd179f6b73ca89cd612a098682a34f7 Mon Sep 17 00:00:00 2001
From: Attila Cseh <77381875+csehatt741@users.noreply.github.com>
Date: Tue, 26 Aug 2025 13:15:13 +0200
Subject: [PATCH] Feature/filter asset sub class options in create activity
dialog (#5404)
* Filter asset sub class options in create activity dialog
* Update changelog
---
CHANGELOG.md | 1 +
.../asset-profile-dialog.component.ts | 6 +--
.../interfaces/interfaces.ts | 7 +--
...ate-or-update-activity-dialog.component.ts | 44 +++++++++++++++----
.../create-or-update-activity-dialog.html | 18 +++++---
.../asset-class-selector-option.interface.ts | 6 +++
libs/common/src/lib/interfaces/index.ts | 2 +
7 files changed, 60 insertions(+), 24 deletions(-)
create mode 100644 libs/common/src/lib/interfaces/asset-class-selector-option.interface.ts
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 808ee3e2c..918d4d82b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
+- Improved the create or update activity dialog’s asset sub class selector for valuables to update the options dynamically based on the selected asset class
- Randomized the minutes of the hourly data gathering cron job
- Refactored the dialog footer component to standalone
- Refactored the dialog header component to standalone
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 0ea28c618..264d0a1b0 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
@@ -13,6 +13,7 @@ import {
import { DATE_FORMAT } from '@ghostfolio/common/helper';
import {
AdminMarketDataDetails,
+ AssetClassSelectorOption,
AssetProfileIdentifier,
LineChartItem,
ScraperConfiguration,
@@ -82,10 +83,7 @@ import ms from 'ms';
import { EMPTY, Subject } from 'rxjs';
import { catchError, takeUntil } from 'rxjs/operators';
-import {
- AssetClassSelectorOption,
- AssetProfileDialogParams
-} from './interfaces/interfaces';
+import { AssetProfileDialogParams } from './interfaces/interfaces';
@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
diff --git a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/interfaces/interfaces.ts b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/interfaces/interfaces.ts
index 0de94c1cb..6a966b427 100644
--- a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/interfaces/interfaces.ts
+++ b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/interfaces/interfaces.ts
@@ -1,11 +1,6 @@
import { ColorScheme } from '@ghostfolio/common/types';
-import { AssetClass, AssetSubClass, DataSource } from '@prisma/client';
-
-export interface AssetClassSelectorOption {
- id: AssetClass | AssetSubClass;
- label: string;
-}
+import { DataSource } from '@prisma/client';
export interface AssetProfileDialogParams {
colorScheme: ColorScheme;
diff --git a/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.component.ts b/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.component.ts
index f2da26246..fde4f20fa 100644
--- a/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.component.ts
+++ b/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.component.ts
@@ -1,8 +1,12 @@
import { CreateOrderDto } from '@ghostfolio/api/app/order/create-order.dto';
import { UpdateOrderDto } from '@ghostfolio/api/app/order/update-order.dto';
import { UserService } from '@ghostfolio/client/services/user/user.service';
+import { ASSET_CLASS_MAPPING } from '@ghostfolio/common/config';
import { getDateFormatString } from '@ghostfolio/common/helper';
-import { LookupItem } from '@ghostfolio/common/interfaces';
+import {
+ AssetClassSelectorOption,
+ LookupItem
+} from '@ghostfolio/common/interfaces';
import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import { GfEntityLogoComponent } from '@ghostfolio/ui/entity-logo';
import { translate } from '@ghostfolio/ui/i18n';
@@ -37,7 +41,7 @@ import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { MatSelectModule } from '@angular/material/select';
import { IonIcon } from '@ionic/angular/standalone';
-import { AssetClass, AssetSubClass, Tag, Type } from '@prisma/client';
+import { AssetClass, Tag, Type } from '@prisma/client';
import { isAfter, isToday } from 'date-fns';
import { addIcons } from 'ionicons';
import { calendarClearOutline, refreshOutline } from 'ionicons/icons';
@@ -73,12 +77,16 @@ import { CreateOrUpdateActivityDialogParams } from './interfaces/interfaces';
})
export class GfCreateOrUpdateActivityDialog implements OnDestroy {
public activityForm: FormGroup;
- public assetClasses = Object.keys(AssetClass).map((assetClass) => {
- return { id: assetClass, label: translate(assetClass) };
- });
- public assetSubClasses = Object.keys(AssetSubClass).map((assetSubClass) => {
- return { id: assetSubClass, label: translate(assetSubClass) };
- });
+
+ public assetClassOptions: AssetClassSelectorOption[] = Object.keys(AssetClass)
+ .map((id) => {
+ return { id, label: translate(id) } as AssetClassSelectorOption;
+ })
+ .sort((a, b) => {
+ return a.label.localeCompare(b.label);
+ });
+
+ public assetSubClassOptions: AssetClassSelectorOption[] = [];
public currencies: string[] = [];
public currencyOfAssetProfile: string;
public currentMarketPrice = null;
@@ -273,6 +281,26 @@ export class GfCreateOrUpdateActivityDialog implements OnDestroy {
}
});
+ this.activityForm
+ .get('assetClass')
+ .valueChanges.pipe(takeUntil(this.unsubscribeSubject))
+ .subscribe((assetClass) => {
+ const assetSubClasses = ASSET_CLASS_MAPPING.get(assetClass) ?? [];
+
+ this.assetSubClassOptions = assetSubClasses
+ .map((assetSubClass) => {
+ return {
+ id: assetSubClass,
+ label: translate(assetSubClass)
+ };
+ })
+ .sort((a, b) => a.label.localeCompare(b.label));
+
+ this.activityForm.get('assetSubClass').setValue(null);
+
+ this.changeDetectorRef.markForCheck();
+ });
+
this.activityForm.get('date').valueChanges.subscribe(() => {
if (isToday(this.activityForm.get('date').value)) {
this.activityForm.get('updateAccountBalance').enable();
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 acd128786..d7f466743 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
@@ -290,9 +290,12 @@
Asset Class
- @for (assetClass of assetClasses; track assetClass) {
- {{
- assetClass.label
+ @for (
+ assetClassOption of assetClassOptions;
+ track assetClassOption.id
+ ) {
+ {{
+ assetClassOption.label
}}
}
@@ -306,9 +309,12 @@
Asset Sub Class
- @for (assetSubClass of assetSubClasses; track assetSubClass) {
- {{
- assetSubClass.label
+ @for (
+ assetSubClassOption of assetSubClassOptions;
+ track assetSubClassOption.id
+ ) {
+ {{
+ assetSubClassOption.label
}}
}
diff --git a/libs/common/src/lib/interfaces/asset-class-selector-option.interface.ts b/libs/common/src/lib/interfaces/asset-class-selector-option.interface.ts
new file mode 100644
index 000000000..b4b1060e4
--- /dev/null
+++ b/libs/common/src/lib/interfaces/asset-class-selector-option.interface.ts
@@ -0,0 +1,6 @@
+import { AssetClass, AssetSubClass } from '@prisma/client';
+
+export interface AssetClassSelectorOption {
+ id: AssetClass | AssetSubClass;
+ label: string;
+}
diff --git a/libs/common/src/lib/interfaces/index.ts b/libs/common/src/lib/interfaces/index.ts
index 52ca76b3a..6529fa3ef 100644
--- a/libs/common/src/lib/interfaces/index.ts
+++ b/libs/common/src/lib/interfaces/index.ts
@@ -8,6 +8,7 @@ import type {
AdminMarketDataItem
} from './admin-market-data.interface';
import type { AdminUsers } from './admin-users.interface';
+import type { AssetClassSelectorOption } from './asset-class-selector-option.interface';
import type { AssetProfileIdentifier } from './asset-profile-identifier.interface';
import type { BenchmarkMarketDataDetails } from './benchmark-market-data-details.interface';
import type { BenchmarkProperty } from './benchmark-property.interface';
@@ -86,6 +87,7 @@ export {
AdminUsers,
AiPromptResponse,
ApiKeyResponse,
+ AssetClassSelectorOption,
AssetProfileIdentifier,
Benchmark,
BenchmarkMarketDataDetails,