|
@ -2,7 +2,13 @@ import { GfAssetProfileIconComponent } from '@ghostfolio/client/components/asset |
|
|
import { GfSymbolModule } from '@ghostfolio/client/pipes/symbol/symbol.module'; |
|
|
import { GfSymbolModule } from '@ghostfolio/client/pipes/symbol/symbol.module'; |
|
|
import { AdminService } from '@ghostfolio/client/services/admin.service'; |
|
|
import { AdminService } from '@ghostfolio/client/services/admin.service'; |
|
|
import { DataService } from '@ghostfolio/client/services/data.service'; |
|
|
import { DataService } from '@ghostfolio/client/services/data.service'; |
|
|
import { Filter, PortfolioPosition, User } from '@ghostfolio/common/interfaces'; |
|
|
import { getAssetProfileIdentifier } from '@ghostfolio/common/helper'; |
|
|
|
|
|
import { |
|
|
|
|
|
AssetProfileIdentifier, |
|
|
|
|
|
Filter, |
|
|
|
|
|
PortfolioPosition, |
|
|
|
|
|
User |
|
|
|
|
|
} from '@ghostfolio/common/interfaces'; |
|
|
import { DateRange } from '@ghostfolio/common/types'; |
|
|
import { DateRange } from '@ghostfolio/common/types'; |
|
|
import { translate } from '@ghostfolio/ui/i18n'; |
|
|
import { translate } from '@ghostfolio/ui/i18n'; |
|
|
|
|
|
|
|
@ -36,8 +42,7 @@ import { MatFormFieldModule } from '@angular/material/form-field'; |
|
|
import { MatMenuTrigger } from '@angular/material/menu'; |
|
|
import { MatMenuTrigger } from '@angular/material/menu'; |
|
|
import { MatSelectModule } from '@angular/material/select'; |
|
|
import { MatSelectModule } from '@angular/material/select'; |
|
|
import { RouterModule } from '@angular/router'; |
|
|
import { RouterModule } from '@angular/router'; |
|
|
import { Account, AssetClass } from '@prisma/client'; |
|
|
import { Account, AssetClass, DataSource } from '@prisma/client'; |
|
|
import { sortBy } from 'lodash'; |
|
|
|
|
|
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; |
|
|
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; |
|
|
import { EMPTY, Observable, Subject, lastValueFrom } from 'rxjs'; |
|
|
import { EMPTY, Observable, Subject, lastValueFrom } from 'rxjs'; |
|
|
import { |
|
|
import { |
|
@ -135,9 +140,10 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { |
|
|
public filterForm = this.formBuilder.group({ |
|
|
public filterForm = this.formBuilder.group({ |
|
|
account: new FormControl<string>(undefined), |
|
|
account: new FormControl<string>(undefined), |
|
|
assetClass: new FormControl<string>(undefined), |
|
|
assetClass: new FormControl<string>(undefined), |
|
|
holdings: new FormControl<PortfolioPosition>(undefined), |
|
|
holding: new FormControl<PortfolioPosition>(undefined), |
|
|
tag: new FormControl<string>(undefined) |
|
|
tag: new FormControl<string>(undefined) |
|
|
}); |
|
|
}); |
|
|
|
|
|
public holdings: PortfolioPosition[] = []; |
|
|
public isLoading = false; |
|
|
public isLoading = false; |
|
|
public isOpen = false; |
|
|
public isOpen = false; |
|
|
public placeholder = $localize`Find holding...`; |
|
|
public placeholder = $localize`Find holding...`; |
|
@ -147,7 +153,6 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { |
|
|
holdings: [] |
|
|
holdings: [] |
|
|
}; |
|
|
}; |
|
|
public tags: Filter[] = []; |
|
|
public tags: Filter[] = []; |
|
|
public holdings: PortfolioPosition[] = []; |
|
|
|
|
|
|
|
|
|
|
|
private filterTypes: Filter['type'][] = [ |
|
|
private filterTypes: Filter['type'][] = [ |
|
|
'ACCOUNT', |
|
|
'ACCOUNT', |
|
@ -167,7 +172,7 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { |
|
|
) {} |
|
|
) {} |
|
|
|
|
|
|
|
|
public ngOnInit() { |
|
|
public ngOnInit() { |
|
|
this.loadPortfolioHoldings(); |
|
|
this.initializeFilterForm(); |
|
|
|
|
|
|
|
|
this.assetClasses = Object.keys(AssetClass).map((assetClass) => { |
|
|
this.assetClasses = Object.keys(AssetClass).map((assetClass) => { |
|
|
return { |
|
|
return { |
|
@ -276,7 +281,7 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { |
|
|
this.filterForm.enable({ emitEvent: false }); |
|
|
this.filterForm.enable({ emitEvent: false }); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
this.loadPortfolioHoldings(); |
|
|
this.initializeFilterForm(); |
|
|
|
|
|
|
|
|
this.tags = |
|
|
this.tags = |
|
|
this.user?.tags |
|
|
this.user?.tags |
|
@ -336,11 +341,11 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { |
|
|
type: 'ASSET_CLASS' |
|
|
type: 'ASSET_CLASS' |
|
|
}, |
|
|
}, |
|
|
{ |
|
|
{ |
|
|
id: this.filterForm.get('holdings').value?.dataSource, |
|
|
id: this.filterForm.get('holding').value?.dataSource, |
|
|
type: 'DATA_SOURCE' |
|
|
type: 'DATA_SOURCE' |
|
|
}, |
|
|
}, |
|
|
{ |
|
|
{ |
|
|
id: this.filterForm.get('holdings').value?.symbol, |
|
|
id: this.filterForm.get('holding').value?.symbol, |
|
|
type: 'SYMBOL' |
|
|
type: 'SYMBOL' |
|
|
}, |
|
|
}, |
|
|
{ |
|
|
{ |
|
@ -486,40 +491,53 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { |
|
|
); |
|
|
); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
public holdingComparisonFunction(option, value): boolean { |
|
|
public holdingComparisonFunction( |
|
|
|
|
|
option: AssetProfileIdentifier, |
|
|
|
|
|
value: AssetProfileIdentifier |
|
|
|
|
|
): boolean { |
|
|
|
|
|
if (value === null) { |
|
|
|
|
|
return false; |
|
|
|
|
|
} |
|
|
return ( |
|
|
return ( |
|
|
option.dataSource === value?.dataSource && option.symbol === value?.symbol |
|
|
getAssetProfileIdentifier(option) === getAssetProfileIdentifier(value) |
|
|
); |
|
|
); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
private loadPortfolioHoldings() { |
|
|
private initializeFilterForm() { |
|
|
this.dataService |
|
|
this.dataService |
|
|
.fetchPortfolioHoldings({ |
|
|
.fetchPortfolioHoldings({ |
|
|
range: 'max' |
|
|
range: 'max' |
|
|
}) |
|
|
}) |
|
|
.pipe(takeUntil(this.unsubscribeSubject)) |
|
|
.pipe(takeUntil(this.unsubscribeSubject)) |
|
|
.subscribe(({ holdings }) => { |
|
|
.subscribe(({ holdings }) => { |
|
|
this.holdings = sortBy(holdings, ({ name }) => { |
|
|
this.holdings = holdings |
|
|
return name.toLowerCase(); |
|
|
.filter(({ assetSubClass }) => { |
|
|
|
|
|
return assetSubClass !== 'CASH'; |
|
|
|
|
|
}) |
|
|
|
|
|
.sort((a, b) => { |
|
|
|
|
|
return a.name?.localeCompare(b.name); |
|
|
}); |
|
|
}); |
|
|
this.setFormValues(); |
|
|
this.setFilterFormValues(); |
|
|
}); |
|
|
}); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
private setFormValues() { |
|
|
private setFilterFormValues() { |
|
|
const dataSource = this.user?.settings?.['filters.dataSource'] ?? null; |
|
|
const dataSource = this.user?.settings?.[ |
|
|
const symbol = this.user?.settings?.['filters.symbol'] ?? null; |
|
|
'filters.dataSource' |
|
|
const selectedHolding = this.holdings.find( |
|
|
] as DataSource; |
|
|
(holding) => |
|
|
const symbol = this.user?.settings?.['filters.symbol']; |
|
|
(holding.dataSource ?? null) === dataSource && |
|
|
const selectedHolding = this.holdings.find((holding) => { |
|
|
(holding.symbol ?? null) === symbol |
|
|
return ( |
|
|
|
|
|
getAssetProfileIdentifier(holding) === |
|
|
|
|
|
getAssetProfileIdentifier({ dataSource, symbol }) |
|
|
); |
|
|
); |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
this.filterForm.setValue( |
|
|
this.filterForm.setValue( |
|
|
{ |
|
|
{ |
|
|
account: this.user?.settings?.['filters.accounts']?.[0] ?? null, |
|
|
account: this.user?.settings?.['filters.accounts']?.[0] ?? null, |
|
|
assetClass: this.user?.settings?.['filters.assetClasses']?.[0] ?? null, |
|
|
assetClass: this.user?.settings?.['filters.assetClasses']?.[0] ?? null, |
|
|
holdings: selectedHolding ?? null, |
|
|
holding: selectedHolding ?? null, |
|
|
tag: this.user?.settings?.['filters.tags']?.[0] ?? null |
|
|
tag: this.user?.settings?.['filters.tags']?.[0] ?? null |
|
|
}, |
|
|
}, |
|
|
{ |
|
|
{ |
|
|