|
|
|
@ -1,7 +1,9 @@
|
|
|
|
|
import { GfAssetProfileIconComponent } from '@ghostfolio/client/components/asset-profile-icon/asset-profile-icon.component';
|
|
|
|
|
import { GfSymbolModule } from '@ghostfolio/client/pipes/symbol/symbol.module';
|
|
|
|
|
import { AdminService } from '@ghostfolio/client/services/admin.service';
|
|
|
|
|
import { DataService } from '@ghostfolio/client/services/data.service';
|
|
|
|
|
import { Filter, User } from '@ghostfolio/common/interfaces';
|
|
|
|
|
import { getAssetProfileIdentifier } from '@ghostfolio/common/helper';
|
|
|
|
|
import { Filter, PortfolioPosition, User } from '@ghostfolio/common/interfaces';
|
|
|
|
|
import { DateRange } from '@ghostfolio/common/types';
|
|
|
|
|
import { translate } from '@ghostfolio/ui/i18n';
|
|
|
|
|
|
|
|
|
@ -35,7 +37,7 @@ import { MatFormFieldModule } from '@angular/material/form-field';
|
|
|
|
|
import { MatMenuTrigger } from '@angular/material/menu';
|
|
|
|
|
import { MatSelectModule } from '@angular/material/select';
|
|
|
|
|
import { RouterModule } from '@angular/router';
|
|
|
|
|
import { Account, AssetClass } from '@prisma/client';
|
|
|
|
|
import { Account, AssetClass, DataSource } from '@prisma/client';
|
|
|
|
|
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
|
|
|
|
|
import { EMPTY, Observable, Subject, lastValueFrom } from 'rxjs';
|
|
|
|
|
import {
|
|
|
|
@ -61,6 +63,7 @@ import {
|
|
|
|
|
FormsModule,
|
|
|
|
|
GfAssetProfileIconComponent,
|
|
|
|
|
GfAssistantListItemComponent,
|
|
|
|
|
GfSymbolModule,
|
|
|
|
|
MatButtonModule,
|
|
|
|
|
MatFormFieldModule,
|
|
|
|
|
MatSelectModule,
|
|
|
|
@ -132,8 +135,10 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit {
|
|
|
|
|
public filterForm = this.formBuilder.group({
|
|
|
|
|
account: new FormControl<string>(undefined),
|
|
|
|
|
assetClass: new FormControl<string>(undefined),
|
|
|
|
|
holding: new FormControl<PortfolioPosition>(undefined),
|
|
|
|
|
tag: new FormControl<string>(undefined)
|
|
|
|
|
});
|
|
|
|
|
public holdings: PortfolioPosition[] = [];
|
|
|
|
|
public isLoading = false;
|
|
|
|
|
public isOpen = false;
|
|
|
|
|
public placeholder = $localize`Find holding...`;
|
|
|
|
@ -144,7 +149,13 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit {
|
|
|
|
|
};
|
|
|
|
|
public tags: Filter[] = [];
|
|
|
|
|
|
|
|
|
|
private filterTypes: Filter['type'][] = ['ACCOUNT', 'ASSET_CLASS', 'TAG'];
|
|
|
|
|
private filterTypes: Filter['type'][] = [
|
|
|
|
|
'ACCOUNT',
|
|
|
|
|
'ASSET_CLASS',
|
|
|
|
|
'DATA_SOURCE',
|
|
|
|
|
'SYMBOL',
|
|
|
|
|
'TAG'
|
|
|
|
|
];
|
|
|
|
|
private keyManager: FocusKeyManager<GfAssistantListItemComponent>;
|
|
|
|
|
private unsubscribeSubject = new Subject<void>();
|
|
|
|
|
|
|
|
|
@ -156,6 +167,8 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit {
|
|
|
|
|
) {}
|
|
|
|
|
|
|
|
|
|
public ngOnInit() {
|
|
|
|
|
this.initializeFilterForm();
|
|
|
|
|
|
|
|
|
|
this.assetClasses = Object.keys(AssetClass).map((assetClass) => {
|
|
|
|
|
return {
|
|
|
|
|
id: assetClass,
|
|
|
|
@ -263,16 +276,7 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit {
|
|
|
|
|
this.filterForm.enable({ emitEvent: false });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
emitEvent: false
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
this.initializeFilterForm();
|
|
|
|
|
|
|
|
|
|
this.tags =
|
|
|
|
|
this.user?.tags
|
|
|
|
@ -298,6 +302,19 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit {
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public holdingComparisonFunction(
|
|
|
|
|
option: PortfolioPosition,
|
|
|
|
|
value: PortfolioPosition
|
|
|
|
|
): boolean {
|
|
|
|
|
if (value === null) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
getAssetProfileIdentifier(option) === getAssetProfileIdentifier(value)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async initialize() {
|
|
|
|
|
this.isLoading = true;
|
|
|
|
|
this.keyManager = new FocusKeyManager(this.assistantListItems).withWrap();
|
|
|
|
@ -331,6 +348,14 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit {
|
|
|
|
|
id: this.filterForm.get('assetClass').value,
|
|
|
|
|
type: 'ASSET_CLASS'
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
id: this.filterForm.get('holding').value?.dataSource,
|
|
|
|
|
type: 'DATA_SOURCE'
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
id: this.filterForm.get('holding').value?.symbol,
|
|
|
|
|
type: 'SYMBOL'
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
id: this.filterForm.get('tag').value,
|
|
|
|
|
type: 'TAG'
|
|
|
|
@ -473,4 +498,47 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit {
|
|
|
|
|
takeUntil(this.unsubscribeSubject)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private initializeFilterForm() {
|
|
|
|
|
this.dataService
|
|
|
|
|
.fetchPortfolioHoldings()
|
|
|
|
|
.pipe(takeUntil(this.unsubscribeSubject))
|
|
|
|
|
.subscribe(({ holdings }) => {
|
|
|
|
|
this.holdings = holdings
|
|
|
|
|
.filter(({ assetSubClass }) => {
|
|
|
|
|
return !['CASH'].includes(assetSubClass);
|
|
|
|
|
})
|
|
|
|
|
.sort((a, b) => {
|
|
|
|
|
return a.name?.localeCompare(b.name);
|
|
|
|
|
});
|
|
|
|
|
this.setFilterFormValues();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private setFilterFormValues() {
|
|
|
|
|
const dataSource = this.user?.settings?.[
|
|
|
|
|
'filters.dataSource'
|
|
|
|
|
] as DataSource;
|
|
|
|
|
const symbol = this.user?.settings?.['filters.symbol'];
|
|
|
|
|
const selectedHolding = this.holdings.find((holding) => {
|
|
|
|
|
return (
|
|
|
|
|
getAssetProfileIdentifier({
|
|
|
|
|
dataSource: holding.dataSource,
|
|
|
|
|
symbol: holding.symbol
|
|
|
|
|
}) === getAssetProfileIdentifier({ dataSource, symbol })
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
this.filterForm.setValue(
|
|
|
|
|
{
|
|
|
|
|
account: this.user?.settings?.['filters.accounts']?.[0] ?? null,
|
|
|
|
|
assetClass: this.user?.settings?.['filters.assetClasses']?.[0] ?? null,
|
|
|
|
|
holding: selectedHolding ?? null,
|
|
|
|
|
tag: this.user?.settings?.['filters.tags']?.[0] ?? null
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
emitEvent: false
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|