From e928141663aa353dff904a23bf1e22d5a793a5f6 Mon Sep 17 00:00:00 2001 From: David Requeno Date: Tue, 12 Aug 2025 23:09:47 -0600 Subject: [PATCH] Feature/assistant accounts Feature/extend search in assistant by accounts --- .../assistant-list-item.component.ts | 16 +-- .../src/lib/assistant/assistant.component.ts | 120 +++++++++--------- libs/ui/src/lib/assistant/assistant.html | 4 +- .../lib/assistant/interfaces/interfaces.ts | 15 ++- 4 files changed, 78 insertions(+), 77 deletions(-) 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 69eb4d9b2..aadb01281 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 @@ -50,7 +50,14 @@ export class GfAssistantListItemComponent public constructor(private changeDetectorRef: ChangeDetectorRef) {} public ngOnChanges() { - if (this.item?.mode === SearchMode.ASSET_PROFILE) { + if (this.item?.mode === SearchMode.ACCOUNT) { + this.queryParams = { + accountDetailDialog: true, + accountId: this.item.id + }; + + this.routerLink = internalRoutes.accounts.routerLink; + } else if (this.item?.mode === SearchMode.ASSET_PROFILE) { this.queryParams = { assetProfileDialog: true, dataSource: this.item?.dataSource, @@ -67,13 +74,6 @@ 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 78e298a13..e5d0dd6da 100644 --- a/libs/ui/src/lib/assistant/assistant.component.ts +++ b/libs/ui/src/lib/assistant/assistant.component.ts @@ -153,8 +153,8 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { }); public holdings: PortfolioPosition[] = []; public isLoading = { - assetProfiles: false, accounts: false, + assetProfiles: false, holdings: false, quickLinks: false }; @@ -162,8 +162,8 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { public placeholder = $localize`Find account, holding or page...`; public searchFormControl = new FormControl(''); public searchResults: ISearchResults = { - assetProfiles: [], accounts: [], + assetProfiles: [], holdings: [], quickLinks: [] }; @@ -201,14 +201,14 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { .pipe( map((searchTerm) => { this.isLoading = { - assetProfiles: true, accounts: true, + assetProfiles: true, holdings: true, quickLinks: true }; this.searchResults = { - assetProfiles: [], accounts: [], + assetProfiles: [], holdings: [], quickLinks: [] }; @@ -221,8 +221,8 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { distinctUntilChanged(), switchMap((searchTerm) => { const results = { - assetProfiles: [], accounts: [], + assetProfiles: [], holdings: [], quickLinks: [] } as ISearchResults; @@ -231,8 +231,8 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { return of(results).pipe( tap(() => { this.isLoading = { - assetProfiles: false, accounts: false, + assetProfiles: false, holdings: false, quickLinks: false }; @@ -240,6 +240,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(); + }) + ); + // Asset profiles const assetProfiles$: Observable> = this .hasPermissionToAccessAdminControl @@ -269,25 +288,6 @@ 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( @@ -324,15 +324,15 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { ); // Merge all results - return merge(quickLinks$, assetProfiles$, accounts$, holdings$).pipe( + return merge(accounts$, assetProfiles$, holdings$, quickLinks$).pipe( scan( (acc: ISearchResults, curr: Partial) => ({ ...acc, ...curr }), { - assetProfiles: [], accounts: [], + assetProfiles: [], holdings: [], quickLinks: [] } as ISearchResults @@ -349,8 +349,8 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { error: (error) => { console.error('Assistant search stream error:', error); this.searchResults = { - assetProfiles: [], accounts: [], + assetProfiles: [], holdings: [], quickLinks: [] }; @@ -358,8 +358,8 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { }, complete: () => { this.isLoading = { - assetProfiles: false, accounts: false, + assetProfiles: false, holdings: false, quickLinks: false }; @@ -479,15 +479,15 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { public initialize() { this.isLoading = { - assetProfiles: true, accounts: true, + assetProfiles: true, holdings: true, quickLinks: true }; this.keyManager = new FocusKeyManager(this.assistantListItems).withWrap(); this.searchResults = { - assetProfiles: [], accounts: [], + assetProfiles: [], holdings: [], quickLinks: [] }; @@ -502,8 +502,8 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { }); this.isLoading = { - assetProfiles: false, accounts: false, + assetProfiles: false, holdings: false, quickLinks: false }; @@ -595,6 +595,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 }) => { + return { + id, + name, + routerLink: internalRoutes.accounts.routerLink, + mode: SearchMode.ACCOUNT as const + }; + }); + }), + takeUntil(this.unsubscribeSubject) + ); + } + private searchAssetProfiles( aSearchTerm: string ): Observable { @@ -662,34 +690,6 @@ 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 f104f762c..5954ce369 100644 --- a/libs/ui/src/lib/assistant/assistant.html +++ b/libs/ui/src/lib/assistant/assistant.html @@ -39,12 +39,12 @@ @if (searchFormControl.value) {
@if ( - !isLoading.assetProfiles && !isLoading.accounts && + !isLoading.assetProfiles && !isLoading.holdings && !isLoading.quickLinks && - searchResults.assetProfiles?.length === 0 && searchResults.accounts?.length === 0 && + searchResults.assetProfiles?.length === 0 && searchResults.holdings?.length === 0 && searchResults.quickLinks?.length === 0 ) { diff --git a/libs/ui/src/lib/assistant/interfaces/interfaces.ts b/libs/ui/src/lib/assistant/interfaces/interfaces.ts index d24fb6b33..247641094 100644 --- a/libs/ui/src/lib/assistant/interfaces/interfaces.ts +++ b/libs/ui/src/lib/assistant/interfaces/interfaces.ts @@ -3,6 +3,12 @@ import { AccountWithValue, DateRange } from '@ghostfolio/common/types'; import { SearchMode } from '../enums/search-mode'; +export interface IAccountSearchResultItem + extends Pick { + mode: SearchMode.ACCOUNT; + routerLink: string[]; +} + export interface IAssetSearchResultItem extends AssetProfileIdentifier { assetSubClassString: string; currency: string; @@ -21,19 +27,14 @@ export interface IQuickLinkSearchResultItem { routerLink: string[]; } -export interface IAccountSearchResultItem - extends Pick { - mode: SearchMode.ACCOUNT; -} - export type ISearchResultItem = - | IAssetSearchResultItem | IAccountSearchResultItem + | IAssetSearchResultItem | IQuickLinkSearchResultItem; export interface ISearchResults { - assetProfiles: ISearchResultItem[]; accounts: ISearchResultItem[]; + assetProfiles: ISearchResultItem[]; holdings: ISearchResultItem[]; quickLinks: ISearchResultItem[]; }