Browse Source

Feature/assistant accounts Feature/extend search in assistant by accounts

pull/5356/head
David Requeno 1 week ago
committed by Thomas Kaul
parent
commit
e928141663
  1. 16
      libs/ui/src/lib/assistant/assistant-list-item/assistant-list-item.component.ts
  2. 120
      libs/ui/src/lib/assistant/assistant.component.ts
  3. 4
      libs/ui/src/lib/assistant/assistant.html
  4. 15
      libs/ui/src/lib/assistant/interfaces/interfaces.ts

16
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 constructor(private changeDetectorRef: ChangeDetectorRef) {}
public ngOnChanges() { 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 = { this.queryParams = {
assetProfileDialog: true, assetProfileDialog: true,
dataSource: this.item?.dataSource, dataSource: this.item?.dataSource,
@ -67,13 +74,6 @@ export class GfAssistantListItemComponent
}; };
this.routerLink = []; 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) { } else if (this.item?.mode === SearchMode.QUICK_LINK) {
this.queryParams = {}; this.queryParams = {};
this.routerLink = this.item.routerLink; this.routerLink = this.item.routerLink;

120
libs/ui/src/lib/assistant/assistant.component.ts

@ -153,8 +153,8 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit {
}); });
public holdings: PortfolioPosition[] = []; public holdings: PortfolioPosition[] = [];
public isLoading = { public isLoading = {
assetProfiles: false,
accounts: false, accounts: false,
assetProfiles: false,
holdings: false, holdings: false,
quickLinks: false quickLinks: false
}; };
@ -162,8 +162,8 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit {
public placeholder = $localize`Find account, holding or page...`; public placeholder = $localize`Find account, holding or page...`;
public searchFormControl = new FormControl(''); public searchFormControl = new FormControl('');
public searchResults: ISearchResults = { public searchResults: ISearchResults = {
assetProfiles: [],
accounts: [], accounts: [],
assetProfiles: [],
holdings: [], holdings: [],
quickLinks: [] quickLinks: []
}; };
@ -201,14 +201,14 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit {
.pipe( .pipe(
map((searchTerm) => { map((searchTerm) => {
this.isLoading = { this.isLoading = {
assetProfiles: true,
accounts: true, accounts: true,
assetProfiles: true,
holdings: true, holdings: true,
quickLinks: true quickLinks: true
}; };
this.searchResults = { this.searchResults = {
assetProfiles: [],
accounts: [], accounts: [],
assetProfiles: [],
holdings: [], holdings: [],
quickLinks: [] quickLinks: []
}; };
@ -221,8 +221,8 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit {
distinctUntilChanged(), distinctUntilChanged(),
switchMap((searchTerm) => { switchMap((searchTerm) => {
const results = { const results = {
assetProfiles: [],
accounts: [], accounts: [],
assetProfiles: [],
holdings: [], holdings: [],
quickLinks: [] quickLinks: []
} as ISearchResults; } as ISearchResults;
@ -231,8 +231,8 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit {
return of(results).pipe( return of(results).pipe(
tap(() => { tap(() => {
this.isLoading = { this.isLoading = {
assetProfiles: false,
accounts: false, accounts: false,
assetProfiles: false,
holdings: false, holdings: false,
quickLinks: false quickLinks: false
}; };
@ -240,6 +240,25 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit {
); );
} }
// Accounts
const accounts$: Observable<Partial<ISearchResults>> =
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 // Asset profiles
const assetProfiles$: Observable<Partial<ISearchResults>> = this const assetProfiles$: Observable<Partial<ISearchResults>> = this
.hasPermissionToAccessAdminControl .hasPermissionToAccessAdminControl
@ -269,25 +288,6 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit {
}) })
); );
// Accounts
const accounts$: Observable<Partial<ISearchResults>> =
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 // Holdings
const holdings$: Observable<Partial<ISearchResults>> = const holdings$: Observable<Partial<ISearchResults>> =
this.searchHoldings(searchTerm).pipe( this.searchHoldings(searchTerm).pipe(
@ -324,15 +324,15 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit {
); );
// Merge all results // Merge all results
return merge(quickLinks$, assetProfiles$, accounts$, holdings$).pipe( return merge(accounts$, assetProfiles$, holdings$, quickLinks$).pipe(
scan( scan(
(acc: ISearchResults, curr: Partial<ISearchResults>) => ({ (acc: ISearchResults, curr: Partial<ISearchResults>) => ({
...acc, ...acc,
...curr ...curr
}), }),
{ {
assetProfiles: [],
accounts: [], accounts: [],
assetProfiles: [],
holdings: [], holdings: [],
quickLinks: [] quickLinks: []
} as ISearchResults } as ISearchResults
@ -349,8 +349,8 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit {
error: (error) => { error: (error) => {
console.error('Assistant search stream error:', error); console.error('Assistant search stream error:', error);
this.searchResults = { this.searchResults = {
assetProfiles: [],
accounts: [], accounts: [],
assetProfiles: [],
holdings: [], holdings: [],
quickLinks: [] quickLinks: []
}; };
@ -358,8 +358,8 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit {
}, },
complete: () => { complete: () => {
this.isLoading = { this.isLoading = {
assetProfiles: false,
accounts: false, accounts: false,
assetProfiles: false,
holdings: false, holdings: false,
quickLinks: false quickLinks: false
}; };
@ -479,15 +479,15 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit {
public initialize() { public initialize() {
this.isLoading = { this.isLoading = {
assetProfiles: true,
accounts: true, accounts: true,
assetProfiles: true,
holdings: true, holdings: true,
quickLinks: true quickLinks: true
}; };
this.keyManager = new FocusKeyManager(this.assistantListItems).withWrap(); this.keyManager = new FocusKeyManager(this.assistantListItems).withWrap();
this.searchResults = { this.searchResults = {
assetProfiles: [],
accounts: [], accounts: [],
assetProfiles: [],
holdings: [], holdings: [],
quickLinks: [] quickLinks: []
}; };
@ -502,8 +502,8 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit {
}); });
this.isLoading = { this.isLoading = {
assetProfiles: false,
accounts: false, accounts: false,
assetProfiles: false,
holdings: false, holdings: false,
quickLinks: false quickLinks: false
}; };
@ -595,6 +595,34 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit {
}); });
} }
private searchAccounts(aSearchTerm: string): Observable<ISearchResultItem[]> {
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( private searchAssetProfiles(
aSearchTerm: string aSearchTerm: string
): Observable<ISearchResultItem[]> { ): Observable<ISearchResultItem[]> {
@ -662,34 +690,6 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit {
); );
} }
private searchAccounts(aSearchTerm: string): Observable<ISearchResultItem[]> {
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[] { private searchQuickLinks(aSearchTerm: string): ISearchResultItem[] {
const searchTerm = aSearchTerm.toLowerCase(); const searchTerm = aSearchTerm.toLowerCase();

4
libs/ui/src/lib/assistant/assistant.html

@ -39,12 +39,12 @@
@if (searchFormControl.value) { @if (searchFormControl.value) {
<div class="overflow-auto py-2 result-container"> <div class="overflow-auto py-2 result-container">
@if ( @if (
!isLoading.assetProfiles &&
!isLoading.accounts && !isLoading.accounts &&
!isLoading.assetProfiles &&
!isLoading.holdings && !isLoading.holdings &&
!isLoading.quickLinks && !isLoading.quickLinks &&
searchResults.assetProfiles?.length === 0 &&
searchResults.accounts?.length === 0 && searchResults.accounts?.length === 0 &&
searchResults.assetProfiles?.length === 0 &&
searchResults.holdings?.length === 0 && searchResults.holdings?.length === 0 &&
searchResults.quickLinks?.length === 0 searchResults.quickLinks?.length === 0
) { ) {

15
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'; import { SearchMode } from '../enums/search-mode';
export interface IAccountSearchResultItem
extends Pick<AccountWithValue, 'id' | 'name'> {
mode: SearchMode.ACCOUNT;
routerLink: string[];
}
export interface IAssetSearchResultItem extends AssetProfileIdentifier { export interface IAssetSearchResultItem extends AssetProfileIdentifier {
assetSubClassString: string; assetSubClassString: string;
currency: string; currency: string;
@ -21,19 +27,14 @@ export interface IQuickLinkSearchResultItem {
routerLink: string[]; routerLink: string[];
} }
export interface IAccountSearchResultItem
extends Pick<AccountWithValue, 'id' | 'name' | 'platform'> {
mode: SearchMode.ACCOUNT;
}
export type ISearchResultItem = export type ISearchResultItem =
| IAssetSearchResultItem
| IAccountSearchResultItem | IAccountSearchResultItem
| IAssetSearchResultItem
| IQuickLinkSearchResultItem; | IQuickLinkSearchResultItem;
export interface ISearchResults { export interface ISearchResults {
assetProfiles: ISearchResultItem[];
accounts: ISearchResultItem[]; accounts: ISearchResultItem[];
assetProfiles: ISearchResultItem[];
holdings: ISearchResultItem[]; holdings: ISearchResultItem[];
quickLinks: ISearchResultItem[]; quickLinks: ISearchResultItem[];
} }

Loading…
Cancel
Save