Browse Source

Feature/filter asset sub class options based on selected asset class (#5148)

* Filter asset sub class options based on selected asset class

* Update changelog
pull/5156/head
Attila Cseh 2 weeks ago
committed by GitHub
parent
commit
2f118d02b8
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 1
      CHANGELOG.md
  2. 48
      apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts
  3. 26
      apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html
  4. 7
      apps/client/src/app/components/admin-market-data/asset-profile-dialog/interfaces/interfaces.ts
  5. 19
      libs/common/src/lib/config.ts

1
CHANGELOG.md

@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
- Improved the portfolio calculations for activities without historical market data
- Improved the asset profile dialog’s asset sub class selector of the admin control panel to update the options dynamically based on the selected asset class
- Improved the asset profile dialog’s data gathering checkbox of the admin control panel to reflect the global settings
- Improved the language localization for Catalan (`ca`)
- Improved the language localization for Chinese (`zh`)

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

@ -6,6 +6,7 @@ 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,
ghostfolioScraperApiSymbolPrefix,
PROPERTY_IS_DATA_GATHERING_ENABLED
} from '@ghostfolio/common/config';
@ -56,7 +57,10 @@ import ms from 'ms';
import { EMPTY, Subject } from 'rxjs';
import { catchError, takeUntil } from 'rxjs/operators';
import { AssetProfileDialogParams } from './interfaces/interfaces';
import {
AssetClassSelectorOption,
AssetProfileDialogParams
} from './interfaces/interfaces';
@Component({
host: { class: 'd-flex flex-column h-100' },
@ -75,15 +79,18 @@ export class AssetProfileDialog implements OnDestroy, OnInit {
@ViewChild('assetProfileFormElement')
assetProfileFormElement: ElementRef<HTMLFormElement>;
public assetProfileClass: string;
public assetClassLabel: string;
public assetSubClassLabel: string;
public assetClasses = Object.keys(AssetClass).map((assetClass) => {
return { id: assetClass, label: translate(assetClass) };
});
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 assetSubClasses = Object.keys(AssetSubClass).map((assetSubClass) => {
return { id: assetSubClass, label: translate(assetSubClass) };
});
public assetSubClassOptions: AssetClassSelectorOption[] = [];
public assetProfile: AdminMarketDataDetails['assetProfile'];
@ -125,7 +132,6 @@ export class AssetProfileDialog implements OnDestroy, OnInit {
}
);
public assetProfileSubClass: string;
public benchmarks: Partial<SymbolProfile>[];
public countries: {
@ -218,6 +224,26 @@ export class AssetProfileDialog implements OnDestroy, OnInit {
}
});
this.assetProfileForm
.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.assetProfileForm.get('assetSubClass').setValue(null);
this.changeDetectorRef.markForCheck();
});
this.dataService
.fetchMarketDataBySymbol({
dataSource: this.data.dataSource,
@ -227,8 +253,8 @@ export class AssetProfileDialog implements OnDestroy, OnInit {
.subscribe(({ assetProfile, marketData }) => {
this.assetProfile = assetProfile;
this.assetProfileClass = translate(this.assetProfile?.assetClass);
this.assetProfileSubClass = translate(this.assetProfile?.assetSubClass);
this.assetClassLabel = translate(this.assetProfile?.assetClass);
this.assetSubClassLabel = translate(this.assetProfile?.assetSubClass);
this.countries = {};
this.isBenchmark = this.benchmarks.some(({ id }) => {

26
apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html

@ -209,8 +209,8 @@
<gf-value
i18n
size="medium"
[hidden]="!assetProfileClass"
[value]="assetProfileClass"
[hidden]="!assetClassLabel"
[value]="assetClassLabel"
>Asset Class</gf-value
>
</div>
@ -218,8 +218,8 @@
<gf-value
i18n
size="medium"
[hidden]="!assetProfileSubClass"
[value]="assetProfileSubClass"
[hidden]="!assetSubClassLabel"
[value]="assetSubClassLabel"
>Asset Sub Class</gf-value
>
</div>
@ -304,9 +304,12 @@
<mat-label i18n>Asset Class</mat-label>
<mat-select formControlName="assetClass">
<mat-option [value]="null" />
@for (assetClass of assetClasses; track assetClass) {
<mat-option [value]="assetClass.id">{{
assetClass.label
@for (
assetClassOption of assetClassOptions;
track assetClassOption.id
) {
<mat-option [value]="assetClassOption.id">{{
assetClassOption.label
}}</mat-option>
}
</mat-select>
@ -317,9 +320,12 @@
<mat-label i18n>Asset Sub Class</mat-label>
<mat-select formControlName="assetSubClass">
<mat-option [value]="null" />
@for (assetSubClass of assetSubClasses; track assetSubClass) {
<mat-option [value]="assetSubClass.id">{{
assetSubClass.label
@for (
assetSubClassOption of assetSubClassOptions;
track assetSubClassOption.id
) {
<mat-option [value]="assetSubClassOption.id">{{
assetSubClassOption.label
}}</mat-option>
}
</mat-select>

7
apps/client/src/app/components/admin-market-data/asset-profile-dialog/interfaces/interfaces.ts

@ -1,6 +1,11 @@
import { ColorScheme } from '@ghostfolio/common/types';
import { DataSource } from '@prisma/client';
import { AssetClass, AssetSubClass, DataSource } from '@prisma/client';
export interface AssetClassSelectorOption {
id: AssetClass | AssetSubClass;
label: string;
}
export interface AssetProfileDialogParams {
colorScheme: ColorScheme;

19
libs/common/src/lib/config.ts

@ -1,4 +1,4 @@
import { DataSource } from '@prisma/client';
import { AssetClass, AssetSubClass, DataSource } from '@prisma/client';
import { JobOptions, JobStatus } from 'bull';
import ms from 'ms';
@ -34,6 +34,23 @@ export const warnColorRgb = {
b: 69
};
export const ASSET_CLASS_MAPPING = new Map<AssetClass, AssetSubClass[]>([
[AssetClass.ALTERNATIVE_INVESTMENT, [AssetSubClass.COLLECTIBLE]],
[AssetClass.COMMODITY, [AssetSubClass.PRECIOUS_METAL]],
[
AssetClass.EQUITY,
[
AssetSubClass.ETF,
AssetSubClass.MUTUALFUND,
AssetSubClass.PRIVATE_EQUITY,
AssetSubClass.STOCK
]
],
[AssetClass.FIXED_INCOME, [AssetSubClass.BOND]],
[AssetClass.LIQUIDITY, [AssetSubClass.CRYPTOCURRENCY]],
[AssetClass.REAL_ESTATE, []]
]);
export const CACHE_TTL_NO_CACHE = 1;
export const CACHE_TTL_INFINITE = 0;

Loading…
Cancel
Save