|
|
@ -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 |
|
|
|
} |
|
|
|
); |
|
|
|
} |
|
|
|
} |
|
|
|