diff --git a/libs/ui/src/lib/assistant/assistant-list-item/assistant-list-item.component.ts b/libs/ui/src/lib/assistant/assistant-list-item/assistant-list-item.component.ts index 70b466127..69eb4d9b2 100644 --- a/libs/ui/src/lib/assistant/assistant-list-item/assistant-list-item.component.ts +++ b/libs/ui/src/lib/assistant/assistant-list-item/assistant-list-item.component.ts @@ -67,6 +67,13 @@ export class GfAssistantListItemComponent }; this.routerLink = []; + } else if (this.item?.mode === SearchMode.ACCOUNT) { + this.queryParams = { + accountDetailDialog: true, + accountId: (this.item as any).id + }; + + this.routerLink = internalRoutes.accounts.routerLink; } else if (this.item?.mode === SearchMode.QUICK_LINK) { this.queryParams = {}; this.routerLink = this.item.routerLink; diff --git a/libs/ui/src/lib/assistant/assistant.component.ts b/libs/ui/src/lib/assistant/assistant.component.ts index 032b3222d..78e298a13 100644 --- a/libs/ui/src/lib/assistant/assistant.component.ts +++ b/libs/ui/src/lib/assistant/assistant.component.ts @@ -154,14 +154,16 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { public holdings: PortfolioPosition[] = []; public isLoading = { assetProfiles: false, + accounts: false, holdings: false, quickLinks: false }; public isOpen = false; - public placeholder = $localize`Find holding or page...`; + public placeholder = $localize`Find account, holding or page...`; public searchFormControl = new FormControl(''); public searchResults: ISearchResults = { assetProfiles: [], + accounts: [], holdings: [], quickLinks: [] }; @@ -200,11 +202,13 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { map((searchTerm) => { this.isLoading = { assetProfiles: true, + accounts: true, holdings: true, quickLinks: true }; this.searchResults = { assetProfiles: [], + accounts: [], holdings: [], quickLinks: [] }; @@ -218,6 +222,7 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { switchMap((searchTerm) => { const results = { assetProfiles: [], + accounts: [], holdings: [], quickLinks: [] } as ISearchResults; @@ -227,6 +232,7 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { tap(() => { this.isLoading = { assetProfiles: false, + accounts: false, holdings: false, quickLinks: false }; @@ -263,6 +269,25 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { }) ); + // Accounts + const accounts$: Observable> = + this.searchAccounts(searchTerm).pipe( + map((accounts) => ({ + accounts: accounts.slice( + 0, + GfAssistantComponent.SEARCH_RESULTS_DEFAULT_LIMIT + ) + })), + catchError((error) => { + console.error('Error fetching accounts for assistant:', error); + return of({ accounts: [] as ISearchResultItem[] }); + }), + tap(() => { + this.isLoading.accounts = false; + this.changeDetectorRef.markForCheck(); + }) + ); + // Holdings const holdings$: Observable> = this.searchHoldings(searchTerm).pipe( @@ -299,7 +324,7 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { ); // Merge all results - return merge(quickLinks$, assetProfiles$, holdings$).pipe( + return merge(quickLinks$, assetProfiles$, accounts$, holdings$).pipe( scan( (acc: ISearchResults, curr: Partial) => ({ ...acc, @@ -307,6 +332,7 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { }), { assetProfiles: [], + accounts: [], holdings: [], quickLinks: [] } as ISearchResults @@ -324,6 +350,7 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { console.error('Assistant search stream error:', error); this.searchResults = { assetProfiles: [], + accounts: [], holdings: [], quickLinks: [] }; @@ -332,6 +359,7 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { complete: () => { this.isLoading = { assetProfiles: false, + accounts: false, holdings: false, quickLinks: false }; @@ -452,12 +480,14 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { public initialize() { this.isLoading = { assetProfiles: true, + accounts: true, holdings: true, quickLinks: true }; this.keyManager = new FocusKeyManager(this.assistantListItems).withWrap(); this.searchResults = { assetProfiles: [], + accounts: [], holdings: [], quickLinks: [] }; @@ -473,6 +503,7 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { this.isLoading = { assetProfiles: false, + accounts: false, holdings: false, quickLinks: false }; @@ -631,6 +662,34 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { ); } + private searchAccounts(aSearchTerm: string): Observable { + return this.dataService + .fetchAccounts({ + filters: [ + { + id: aSearchTerm, + type: 'SEARCH_QUERY' + } + ] + }) + .pipe( + catchError(() => { + return EMPTY; + }), + map(({ accounts }) => { + return accounts.map(({ id, name, platform }) => { + return { + id, + name, + platform, + mode: SearchMode.ACCOUNT as const + }; + }); + }), + takeUntil(this.unsubscribeSubject) + ); + } + private searchQuickLinks(aSearchTerm: string): ISearchResultItem[] { const searchTerm = aSearchTerm.toLowerCase(); diff --git a/libs/ui/src/lib/assistant/assistant.html b/libs/ui/src/lib/assistant/assistant.html index f957d9dcc..f104f762c 100644 --- a/libs/ui/src/lib/assistant/assistant.html +++ b/libs/ui/src/lib/assistant/assistant.html @@ -40,9 +40,11 @@
@if ( !isLoading.assetProfiles && + !isLoading.accounts && !isLoading.holdings && !isLoading.quickLinks && searchResults.assetProfiles?.length === 0 && + searchResults.accounts?.length === 0 && searchResults.holdings?.length === 0 && searchResults.quickLinks?.length === 0 ) { @@ -76,6 +78,32 @@ }
} + @if (isLoading.accounts || searchResults?.accounts?.length !== 0) { +
+
+ Accounts +
+ @for ( + searchResultItem of searchResults.accounts; + track searchResultItem + ) { + + } + @if (isLoading.accounts) { + + } +
+ } @if (isLoading.holdings || searchResults?.holdings?.length !== 0) {
diff --git a/libs/ui/src/lib/assistant/enums/search-mode.ts b/libs/ui/src/lib/assistant/enums/search-mode.ts index f85ad47e9..af9c4dd45 100644 --- a/libs/ui/src/lib/assistant/enums/search-mode.ts +++ b/libs/ui/src/lib/assistant/enums/search-mode.ts @@ -1,4 +1,5 @@ export enum SearchMode { + ACCOUNT = 'account', ASSET_PROFILE = 'assetProfile', HOLDING = 'holding', QUICK_LINK = 'quickLink' diff --git a/libs/ui/src/lib/assistant/interfaces/interfaces.ts b/libs/ui/src/lib/assistant/interfaces/interfaces.ts index ce8b644be..d24fb6b33 100644 --- a/libs/ui/src/lib/assistant/interfaces/interfaces.ts +++ b/libs/ui/src/lib/assistant/interfaces/interfaces.ts @@ -1,5 +1,5 @@ import { AssetProfileIdentifier } from '@ghostfolio/common/interfaces'; -import { DateRange } from '@ghostfolio/common/types'; +import { AccountWithValue, DateRange } from '@ghostfolio/common/types'; import { SearchMode } from '../enums/search-mode'; @@ -21,12 +21,19 @@ export interface IQuickLinkSearchResultItem { routerLink: string[]; } +export interface IAccountSearchResultItem + extends Pick { + mode: SearchMode.ACCOUNT; +} + export type ISearchResultItem = | IAssetSearchResultItem + | IAccountSearchResultItem | IQuickLinkSearchResultItem; export interface ISearchResults { assetProfiles: ISearchResultItem[]; + accounts: ISearchResultItem[]; holdings: ISearchResultItem[]; quickLinks: ISearchResultItem[]; }