From c6a0541351a872bc0b09e8d20a0dcf6cf642ea81 Mon Sep 17 00:00:00 2001 From: Valentin Zickner Date: Fri, 7 May 2021 18:37:08 +0200 Subject: [PATCH 1/6] add multi-filter support for transaction filtering with auto completion --- .../interfaces/order-with-account.type.ts | 6 +- .../transactions-table.component.html | 40 ++++-- .../transactions-table.component.ts | 126 +++++++++++++++--- .../transactions-table.module.ts | 8 +- 4 files changed, 153 insertions(+), 27 deletions(-) diff --git a/apps/api/src/app/order/interfaces/order-with-account.type.ts b/apps/api/src/app/order/interfaces/order-with-account.type.ts index d1f5ac552..db14b6dfd 100644 --- a/apps/api/src/app/order/interfaces/order-with-account.type.ts +++ b/apps/api/src/app/order/interfaces/order-with-account.type.ts @@ -1,3 +1,5 @@ -import { Account, Order } from '@prisma/client'; +import { Account, Order, Platform } from '@prisma/client'; -export type OrderWithAccount = Order & { Account?: Account }; +type AccountWithPlatform = Account & { Platform?: Platform }; + +export type OrderWithAccount = Order & { Account?: AccountWithPlatform }; diff --git a/apps/client/src/app/components/transactions-table/transactions-table.component.html b/apps/client/src/app/components/transactions-table/transactions-table.component.html index 9d2165180..4af802911 100644 --- a/apps/client/src/app/components/transactions-table/transactions-table.component.html +++ b/apps/client/src/app/components/transactions-table/transactions-table.component.html @@ -1,12 +1,36 @@ - - + + + + {{ searchKeyword }} + + + + + + + {{ transaction }} + + (); @ViewChild(MatSort) sort: MatSort; + @ViewChild('searchInput') searchInput: ElementRef; + @ViewChild('auto') matAutocomplete: MatAutocomplete; public dataSource: MatTableDataSource = new MatTableDataSource(); public defaultDateFormat = DEFAULT_DATE_FORMAT; public displayedColumns = []; public isLoading = true; public routeQueryParams: Subscription; + public separatorKeysCodes: number[] = [ENTER, COMMA]; + public searchKeywords: string[] = []; + public searchControl = new FormControl(); + public filteredTransactions$: Subject = new BehaviorSubject([]); + public filteredTransactions: Observable< + string[] + > = this.filteredTransactions$.asObservable(); + private allFilteredTransactions: string[]; private unsubscribeSubject = new Subject(); public constructor( @@ -63,6 +83,50 @@ export class TransactionsTableComponent }); } }); + + this.searchControl.valueChanges.subscribe((keyword) => { + if (keyword) { + const filterValue = keyword.toLowerCase(); + this.filteredTransactions$.next( + this.allFilteredTransactions.filter( + (filter) => filter.toLowerCase().indexOf(filterValue) === 0 + ) + ); + } + }); + } + + public addKeyword(event: MatChipInputEvent): void { + const input = event.input; + const value = event.value; + + if ((value || '').trim()) { + this.searchKeywords.push(value.trim()); + this.updateFilter(); + } + + // Reset the input value + if (input) { + input.value = ''; + } + + this.searchControl.setValue(null); + } + + public removeKeyword(keyword: string): void { + const index = this.searchKeywords.indexOf(keyword); + + if (index >= 0) { + this.searchKeywords.splice(index, 1); + this.updateFilter(); + } + } + + public keywordSelected(event: MatAutocompleteSelectedEvent): void { + this.searchKeywords.push(event.option.viewValue); + this.updateFilter(); + this.searchInput.nativeElement.value = ''; + this.searchControl.setValue(null); } public ngOnInit() {} @@ -88,28 +152,22 @@ export class TransactionsTableComponent if (this.transactions) { this.dataSource = new MatTableDataSource(this.transactions); this.dataSource.filterPredicate = (data, filter) => { - const accumulator = (currentTerm: string, key: string) => { - return key === 'Account' - ? currentTerm + data.Account.name - : currentTerm + data[key]; - }; - const dataString = Object.keys(data) - .reduce(accumulator, '') + let dataString = TransactionsTableComponent.getFilterableValues(data) + .join(' ') .toLowerCase(); - const transformedFilter = filter.trim().toLowerCase(); - return dataString.includes(transformedFilter); + let contains = true; + for (const singleFilter of filter.split(SEARCH_STRING_SEPARATOR)) { + contains = + contains && dataString.includes(singleFilter.trim().toLowerCase()); + } + return contains; }; this.dataSource.sort = this.sort; - + this.updateFilter(); this.isLoading = false; } } - public applyFilter(event: Event) { - const filterValue = (event.target as HTMLInputElement).value; - this.dataSource.filter = filterValue.trim().toLowerCase(); - } - public onDeleteTransaction(aId: string) { const confirmation = confirm( 'Do you really want to delete this transaction?' @@ -169,4 +227,40 @@ export class TransactionsTableComponent this.unsubscribeSubject.next(); this.unsubscribeSubject.complete(); } + + private updateFilter() { + this.dataSource.filter = this.searchKeywords.join(SEARCH_STRING_SEPARATOR); + const lowercaseSearchKeywords = this.searchKeywords.map((keyword) => + keyword.trim().toLowerCase() + ); + this.allFilteredTransactions = TransactionsTableComponent.getSearchableFieldValues( + this.transactions + ).filter((item) => { + return !lowercaseSearchKeywords.includes(item.trim().toLowerCase()); + }); + this.filteredTransactions$.next(this.allFilteredTransactions); + } + + private static getSearchableFieldValues( + transactions: OrderWithAccount[] + ): string[] { + const fieldValues = new Set(); + for (const transaction of transactions) { + this.getFilterableValues(transaction, fieldValues); + } + + return [...fieldValues].sort(); + } + + private static getFilterableValues( + transaction, + fieldValues: Set = new Set() + ): string[] { + fieldValues.add(transaction.currency); + fieldValues.add(transaction.symbol); + fieldValues.add(transaction.type); + fieldValues.add(transaction.Account?.name); + fieldValues.add(transaction.Account?.Platform?.name); + return [...fieldValues]; + } } diff --git a/apps/client/src/app/components/transactions-table/transactions-table.module.ts b/apps/client/src/app/components/transactions-table/transactions-table.module.ts index 34d66e84a..f20a38df6 100644 --- a/apps/client/src/app/components/transactions-table/transactions-table.module.ts +++ b/apps/client/src/app/components/transactions-table/transactions-table.module.ts @@ -13,6 +13,9 @@ import { GfPositionDetailDialogModule } from '../position/position-detail-dialog import { GfSymbolIconModule } from '../symbol-icon/symbol-icon.module'; import { GfValueModule } from '../value/value.module'; import { TransactionsTableComponent } from './transactions-table.component'; +import { MatChipsModule } from '@angular/material/chips'; +import { MatAutocompleteModule } from '@angular/material/autocomplete'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; @NgModule({ declarations: [TransactionsTableComponent], @@ -23,13 +26,16 @@ import { TransactionsTableComponent } from './transactions-table.component'; GfSymbolIconModule, GfSymbolModule, GfValueModule, + MatAutocompleteModule, MatButtonModule, + MatChipsModule, MatInputModule, MatMenuModule, MatSortModule, MatTableModule, NgxSkeletonLoaderModule, - RouterModule + RouterModule, + ReactiveFormsModule ], providers: [], schemas: [CUSTOM_ELEMENTS_SCHEMA] From 2531029ceb6795629a8e43e646dd98a120fbd1e7 Mon Sep 17 00:00:00 2001 From: Valentin Zickner Date: Fri, 7 May 2021 18:37:54 +0200 Subject: [PATCH 2/6] update changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 92ddaa77d..4cd613412 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fixed the filtering by account name in the transactions table +### Added + +- Improved transaction filtering with multi filter support + ## 1.0.0 - 05.05.2021 ### Added From 279bcf9375fa77bcc206be68d752598def52af9c Mon Sep 17 00:00:00 2001 From: Valentin Zickner Date: Sat, 8 May 2021 19:18:56 +0200 Subject: [PATCH 3/6] fix table for transaction for accounts without platform --- .../transactions-table/transactions-table.component.html | 1 + .../transactions-table/transactions-table.component.ts | 9 +++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/apps/client/src/app/components/transactions-table/transactions-table.component.html b/apps/client/src/app/components/transactions-table/transactions-table.component.html index 4af802911..d4dddbe20 100644 --- a/apps/client/src/app/components/transactions-table/transactions-table.component.html +++ b/apps/client/src/app/components/transactions-table/transactions-table.component.html @@ -12,6 +12,7 @@ filter.toLowerCase().indexOf(filterValue) === 0 ) ); + } else { + this.filteredTransactions$.next(this.allFilteredTransactions); } }); } @@ -249,7 +251,9 @@ export class TransactionsTableComponent this.getFilterableValues(transaction, fieldValues); } - return [...fieldValues].sort(); + return [...fieldValues] + .filter(item => item != undefined) + .sort(); } private static getFilterableValues( @@ -261,6 +265,7 @@ export class TransactionsTableComponent fieldValues.add(transaction.type); fieldValues.add(transaction.Account?.name); fieldValues.add(transaction.Account?.Platform?.name); - return [...fieldValues]; + return [...fieldValues] + .filter(item => item != undefined); } } From 455671898ae30d9ac2f019e3ab1cfb394baef508 Mon Sep 17 00:00:00 2001 From: Thomas <4159106+dtslvr@users.noreply.github.com> Date: Sun, 9 May 2021 21:53:35 +0200 Subject: [PATCH 4/6] Improve UI --- .../transactions-table.component.html | 8 +- .../transactions-table.component.scss | 4 + .../transactions-table.module.ts | 6 +- yarn.lock | 668 ++++++++++-------- 4 files changed, 368 insertions(+), 318 deletions(-) diff --git a/apps/client/src/app/components/transactions-table/transactions-table.component.html b/apps/client/src/app/components/transactions-table/transactions-table.component.html index d4dddbe20..0d6a99dba 100644 --- a/apps/client/src/app/components/transactions-table/transactions-table.component.html +++ b/apps/client/src/app/components/transactions-table/transactions-table.component.html @@ -1,4 +1,4 @@ - + {{ searchKeyword }} - + Date: Sun, 9 May 2021 22:03:26 +0200 Subject: [PATCH 5/6] Refactoring --- .../transactions-table.component.html | 6 ++--- .../transactions-table.component.ts | 25 ++++++++----------- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/apps/client/src/app/components/transactions-table/transactions-table.component.html b/apps/client/src/app/components/transactions-table/transactions-table.component.html index 0d6a99dba..b7d51f79c 100644 --- a/apps/client/src/app/components/transactions-table/transactions-table.component.html +++ b/apps/client/src/app/components/transactions-table/transactions-table.component.html @@ -13,16 +13,16 @@ (); @Output() transactionToUpdate = new EventEmitter(); - @ViewChild(MatSort) sort: MatSort; + @ViewChild('autocomplete') matAutocomplete: MatAutocomplete; @ViewChild('searchInput') searchInput: ElementRef; - @ViewChild('auto') matAutocomplete: MatAutocomplete; + @ViewChild(MatSort) sort: MatSort; public dataSource: MatTableDataSource = new MatTableDataSource(); public defaultDateFormat = DEFAULT_DATE_FORMAT; public displayedColumns = []; - public isLoading = true; - public routeQueryParams: Subscription; - public separatorKeysCodes: number[] = [ENTER, COMMA]; - public searchKeywords: string[] = []; - public searchControl = new FormControl(); public filteredTransactions$: Subject = new BehaviorSubject([]); public filteredTransactions: Observable< string[] > = this.filteredTransactions$.asObservable(); + public isLoading = true; + public routeQueryParams: Subscription; + public searchKeywords: string[] = []; + public searchControl = new FormControl(); + public separatorKeysCodes: number[] = [ENTER, COMMA]; private allFilteredTransactions: string[]; private unsubscribeSubject = new Subject(); @@ -102,7 +102,7 @@ export class TransactionsTableComponent const input = event.input; const value = event.value; - if ((value || '').trim()) { + if (value?.trim()) { this.searchKeywords.push(value.trim()); this.updateFilter(); } @@ -154,7 +154,7 @@ export class TransactionsTableComponent if (this.transactions) { this.dataSource = new MatTableDataSource(this.transactions); this.dataSource.filterPredicate = (data, filter) => { - let dataString = TransactionsTableComponent.getFilterableValues(data) + const dataString = TransactionsTableComponent.getFilterableValues(data) .join(' ') .toLowerCase(); let contains = true; @@ -251,9 +251,7 @@ export class TransactionsTableComponent this.getFilterableValues(transaction, fieldValues); } - return [...fieldValues] - .filter(item => item != undefined) - .sort(); + return [...fieldValues].filter((item) => item != undefined).sort(); } private static getFilterableValues( @@ -265,7 +263,6 @@ export class TransactionsTableComponent fieldValues.add(transaction.type); fieldValues.add(transaction.Account?.name); fieldValues.add(transaction.Account?.Platform?.name); - return [...fieldValues] - .filter(item => item != undefined); + return [...fieldValues].filter((item) => item != undefined); } } From b2784829f14d29b20c85dd5a0b1d9b8440469a25 Mon Sep 17 00:00:00 2001 From: Thomas <4159106+dtslvr@users.noreply.github.com> Date: Sun, 9 May 2021 22:12:16 +0200 Subject: [PATCH 6/6] Refactoring --- .../transactions-table/transactions-table.component.html | 2 +- .../transactions-table/transactions-table.component.ts | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/apps/client/src/app/components/transactions-table/transactions-table.component.html b/apps/client/src/app/components/transactions-table/transactions-table.component.html index b7d51f79c..ec721b255 100644 --- a/apps/client/src/app/components/transactions-table/transactions-table.component.html +++ b/apps/client/src/app/components/transactions-table/transactions-table.component.html @@ -3,9 +3,9 @@ {{ searchKeyword }} diff --git a/apps/client/src/app/components/transactions-table/transactions-table.component.ts b/apps/client/src/app/components/transactions-table/transactions-table.component.ts index 9f420b048..41e65f261 100644 --- a/apps/client/src/app/components/transactions-table/transactions-table.component.ts +++ b/apps/client/src/app/components/transactions-table/transactions-table.component.ts @@ -61,8 +61,8 @@ export class TransactionsTableComponent > = this.filteredTransactions$.asObservable(); public isLoading = true; public routeQueryParams: Subscription; - public searchKeywords: string[] = []; public searchControl = new FormControl(); + public searchKeywords: string[] = []; public separatorKeysCodes: number[] = [ENTER, COMMA]; private allFilteredTransactions: string[]; @@ -98,10 +98,7 @@ export class TransactionsTableComponent }); } - public addKeyword(event: MatChipInputEvent): void { - const input = event.input; - const value = event.value; - + public addKeyword({ input, value }: MatChipInputEvent): void { if (value?.trim()) { this.searchKeywords.push(value.trim()); this.updateFilter();