Feature/extend assistant by asset class selector (#2957)

* Remove tabs

* Add asset class selector

* Update changelog
pull/2961/head
Thomas Kaul 4 months ago committed by GitHub
parent c68d113d27
commit 06ba7a4b1b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Unreleased
### Added
- Extended the assistant by an asset class selector (experimental)
### Changed
- Improved the usability of the account selector in the assistant (experimental)

@ -42,6 +42,10 @@ export class UpdateUserSettingDto {
@IsOptional()
'filters.accounts'?: string[];
@IsArray()
@IsOptional()
'filters.assetClasses'?: string[];
@IsArray()
@IsOptional()
'filters.tags'?: string[];

@ -170,6 +170,8 @@ export class HeaderComponent implements OnChanges {
if (filter.type === 'ACCOUNT') {
filtersType = 'accounts';
} else if (filter.type === 'ASSET_CLASS') {
filtersType = 'assetClasses';
} else if (filter.type === 'TAG') {
filtersType = 'tags';
}

@ -58,6 +58,13 @@ export class UserService extends ObservableStore<UserStoreState> {
});
}
if (user.settings['filters.assetClasses']) {
filters.push({
id: user.settings['filters.assetClasses'][0],
type: 'ASSET_CLASS'
});
}
if (user.settings['filters.tags']) {
filters.push({
id: user.settings['filters.tags'][0],

@ -22,7 +22,7 @@ import { DataService } from '@ghostfolio/client/services/data.service';
import { Filter, User } from '@ghostfolio/common/interfaces';
import { DateRange } from '@ghostfolio/common/types';
import { translate } from '@ghostfolio/ui/i18n';
import { Account, Tag } from '@prisma/client';
import { Account, AssetClass, Tag } from '@prisma/client';
import { EMPTY, Observable, Subject, lastValueFrom } from 'rxjs';
import {
catchError,
@ -92,6 +92,7 @@ export class AssistantComponent implements OnChanges, OnDestroy, OnInit {
public static readonly SEARCH_RESULTS_DEFAULT_LIMIT = 5;
public accounts: Account[] = [];
public assetClasses: Filter[] = [];
public dateRangeFormControl = new FormControl<string>(undefined);
public readonly dateRangeOptions = [
{ label: $localize`Today`, value: '1d' },
@ -116,6 +117,7 @@ export class AssistantComponent implements OnChanges, OnDestroy, OnInit {
];
public filterForm = this.formBuilder.group({
account: new FormControl<string>(undefined),
assetClass: new FormControl<string>(undefined),
tag: new FormControl<string>(undefined)
});
public isLoading = false;
@ -126,8 +128,9 @@ export class AssistantComponent implements OnChanges, OnDestroy, OnInit {
assetProfiles: [],
holdings: []
};
public tags: Tag[] = [];
public tags: Filter[] = [];
private filterTypes: Filter['type'][] = ['ACCOUNT', 'ASSET_CLASS', 'TAG'];
private keyManager: FocusKeyManager<AssistantListItemComponent>;
private unsubscribeSubject = new Subject<void>();
@ -140,28 +143,38 @@ export class AssistantComponent implements OnChanges, OnDestroy, OnInit {
public ngOnInit() {
this.accounts = this.user?.accounts;
this.assetClasses = Object.keys(AssetClass).map((assetClass) => {
return {
id: assetClass,
label: translate(assetClass),
type: 'ASSET_CLASS'
};
});
this.tags = this.user?.tags.map(({ id, name }) => {
return {
id,
name: translate(name)
label: translate(name),
type: 'TAG'
};
});
this.filterForm.valueChanges
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe(({ account, tag }) => {
.subscribe(({ account, assetClass, tag }) => {
this.filtersChanged.emit([
{
id: account,
type: 'ACCOUNT'
},
{
id: assetClass,
type: 'ASSET_CLASS'
},
{
id: tag,
type: 'TAG'
}
]);
this.onCloseAssistant();
});
this.searchFormControl.valueChanges
@ -209,6 +222,7 @@ export class AssistantComponent implements OnChanges, OnDestroy, OnInit {
this.filterForm.setValue(
{
account: this.user?.settings?.['filters.accounts']?.[0] ?? null,
assetClass: this.user?.settings?.['filters.assetClasses']?.[0] ?? null,
tag: this.user?.settings?.['filters.tags']?.[0] ?? null
},
{
@ -257,16 +271,14 @@ export class AssistantComponent implements OnChanges, OnDestroy, OnInit {
}
public onResetFilters() {
this.filtersChanged.emit([
{
id: null,
type: 'ACCOUNT'
},
{
id: null,
type: 'TAG'
}
]);
this.filtersChanged.emit(
this.filterTypes.map((type) => {
return {
type,
id: null
};
})
);
this.onCloseAssistant();
}

@ -89,9 +89,9 @@
<form [formGroup]="filterForm">
<div
*ngIf="!(isLoading || searchFormControl.value) && user?.settings?.isExperimentalFeatures"
class="filter-container"
class="filter-container p-3"
>
<div class="p-3">
<div class="mb-3">
<mat-form-field appearance="outline" class="w-100 without-hint">
<mat-label i18n>Date Range</mat-label>
<mat-select
@ -104,60 +104,51 @@
</mat-select>
</mat-form-field>
</div>
<mat-tab-group
animationDuration="0"
mat-align-tabs="start"
[mat-stretch-tabs]="false"
>
<mat-tab>
<ng-template mat-tab-label
><ion-icon name="albums-outline" /><span
class="d-none d-sm-block ml-2"
i18n
>Accounts</span
></ng-template
>
<div class="p-3">
<mat-form-field appearance="outline" class="w-100 without-hint">
<mat-select formControlName="account">
<mat-option [value]="null"></mat-option>
@for (account of accounts; track account.id) {
<mat-option [value]="account.id">
<div class="d-flex">
<gf-symbol-icon
*ngIf="account.Platform?.url"
class="mr-1"
[tooltip]="account.Platform?.name"
[url]="account.Platform?.url"
/><span>{{ account.name }}</span>
</div>
</mat-option>
}
</mat-select>
</mat-form-field>
</div>
</mat-tab>
<mat-tab>
<ng-template mat-tab-label
><ion-icon name="pricetag-outline" /><span
class="d-none d-sm-block ml-2"
i18n
>Tags</span
></ng-template
>
<div class="p-3">
<mat-form-field appearance="outline" class="w-100 without-hint">
<mat-select formControlName="tag">
<mat-option [value]="null"></mat-option>
@for (tag of tags; track tag.id) {
<mat-option [value]="tag.id">{{ tag.name }}</mat-option>
}
</mat-select>
</mat-form-field>
</div>
</mat-tab>
</mat-tab-group>
<div class="pb-3 px-3">
<div class="mb-3">
<mat-form-field appearance="outline" class="w-100 without-hint">
<mat-label i18n>Accounts</mat-label>
<mat-select formControlName="account">
<mat-option [value]="null"></mat-option>
@for (account of accounts; track account.id) {
<mat-option [value]="account.id">
<div class="d-flex">
<gf-symbol-icon
*ngIf="account.Platform?.url"
class="mr-1"
[tooltip]="account.Platform?.name"
[url]="account.Platform?.url"
/><span>{{ account.name }}</span>
</div>
</mat-option>
}
</mat-select>
</mat-form-field>
</div>
<div class="mb-3">
<mat-form-field appearance="outline" class="w-100 without-hint">
<mat-label i18n>Tags</mat-label>
<mat-select formControlName="tag">
<mat-option [value]="null"></mat-option>
@for (tag of tags; track tag.id) {
<mat-option [value]="tag.id">{{ tag.label }}</mat-option>
}
</mat-select>
</mat-form-field>
</div>
<div class="mb-3">
<mat-form-field appearance="outline" class="w-100 without-hint">
<mat-label i18n>Asset Classes</mat-label>
<mat-select formControlName="assetClass">
<mat-option [value]="null"></mat-option>
@for (assetClass of assetClasses; track assetClass.id) {
<mat-option [value]="assetClass.id"
>{{ assetClass.label }}</mat-option
>
}
</mat-select>
</mat-form-field>
</div>
<div>
<button
class="w-100"
color="primary"

@ -4,7 +4,6 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatSelectModule } from '@angular/material/select';
import { MatTabsModule } from '@angular/material/tabs';
import { RouterModule } from '@angular/router';
import { GfSymbolIconModule } from '@ghostfolio/client/components/symbol-icon/symbol-icon.module';
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
@ -23,7 +22,6 @@ import { AssistantComponent } from './assistant.component';
MatButtonModule,
MatFormFieldModule,
MatSelectModule,
MatTabsModule,
NgxSkeletonLoaderModule,
ReactiveFormsModule,
RouterModule

Loading…
Cancel
Save