diff --git a/CHANGELOG.md b/CHANGELOG.md index 147a48b1a..fcf3b328b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Added support for notes in the activities import +- Added support to search in the platform selector of the create or update account dialog - Added support for a search query in the portfolio position endpoint - Added the application version to the endpoint `GET api/v1/admin` - Introduced a carousel component for the testimonial section on the landing page diff --git a/apps/api/src/app/info/info.service.ts b/apps/api/src/app/info/info.service.ts index f2c45a72b..4fc4aec4e 100644 --- a/apps/api/src/app/info/info.service.ts +++ b/apps/api/src/app/info/info.service.ts @@ -55,12 +55,8 @@ export class InfoService { public async get(): Promise { const info: Partial = {}; let isReadOnlyMode: boolean; - const platforms = ( - await this.platformService.getPlatforms({ - orderBy: { name: 'asc' } - }) - ).map(({ id, name }) => { - return { id, name }; + const platforms = await this.platformService.getPlatforms({ + orderBy: { name: 'asc' } }); let systemMessage: string; diff --git a/apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.component.ts b/apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.component.ts index 3babc14bc..e2c63f191 100644 --- a/apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.component.ts +++ b/apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.component.ts @@ -4,12 +4,20 @@ import { Inject, OnDestroy } from '@angular/core'; -import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { + AbstractControl, + FormBuilder, + FormGroup, + ValidatorFn, + Validators +} from '@angular/forms'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { CreateAccountDto } from '@ghostfolio/api/app/account/create-account.dto'; import { UpdateAccountDto } from '@ghostfolio/api/app/account/update-account.dto'; import { DataService } from '@ghostfolio/client/services/data.service'; -import { Subject } from 'rxjs'; +import { Platform } from '@prisma/client'; +import { Observable, Subject } from 'rxjs'; +import { map, startWith } from 'rxjs/operators'; import { CreateOrUpdateAccountDialogParams } from './interfaces/interfaces'; @@ -23,7 +31,8 @@ import { CreateOrUpdateAccountDialogParams } from './interfaces/interfaces'; export class CreateOrUpdateAccountDialog implements OnDestroy { public accountForm: FormGroup; public currencies: string[] = []; - public platforms: { id: string; name: string }[]; + public filteredPlatforms: Observable; + public platforms: Platform[]; private unsubscribeSubject = new Subject(); @@ -34,7 +43,7 @@ export class CreateOrUpdateAccountDialog implements OnDestroy { private formBuilder: FormBuilder ) {} - ngOnInit() { + public ngOnInit() { const { currencies, platforms } = this.dataService.fetchInfo(); this.currencies = currencies; @@ -47,8 +56,41 @@ export class CreateOrUpdateAccountDialog implements OnDestroy { currency: [this.data.account.currency, Validators.required], isExcluded: [this.data.account.isExcluded], name: [this.data.account.name, Validators.required], - platformId: [this.data.account.platformId] + platformId: [ + this.platforms.find(({ id }) => { + return id === this.data.account.platformId; + }), + this.autocompleteObjectValidator() + ] }); + + this.filteredPlatforms = this.accountForm + .get('platformId') + .valueChanges.pipe( + startWith(''), + map((value) => { + const name = typeof value === 'string' ? value : value?.name; + return name ? this.filter(name as string) : this.platforms.slice(); + }) + ); + } + + public autoCompleteCheck() { + const inputValue = this.accountForm.controls['platformId'].value; + + if (typeof inputValue === 'string') { + const matchingEntry = this.platforms.find(({ name }) => { + return name === inputValue; + }); + + if (matchingEntry) { + this.accountForm.controls['platformId'].setValue(matchingEntry); + } + } + } + + public displayFn(platform: Platform) { + return platform?.name ?? ''; } public onCancel() { @@ -63,7 +105,7 @@ export class CreateOrUpdateAccountDialog implements OnDestroy { id: this.accountForm.controls['accountId'].value, isExcluded: this.accountForm.controls['isExcluded'].value, name: this.accountForm.controls['name'].value, - platformId: this.accountForm.controls['platformId'].value + platformId: this.accountForm.controls['platformId'].value?.id ?? null }; if (this.data.account.id) { @@ -79,4 +121,22 @@ export class CreateOrUpdateAccountDialog implements OnDestroy { this.unsubscribeSubject.next(); this.unsubscribeSubject.complete(); } + + private autocompleteObjectValidator(): ValidatorFn { + return (control: AbstractControl) => { + if (control.value && typeof control.value === 'string') { + return { invalidAutocompleteObject: { value: control.value } }; + } + + return null; + }; + } + + private filter(value: string): Platform[] { + const filterValue = value.toLowerCase(); + + return this.platforms.filter(({ name }) => { + return name.toLowerCase().startsWith(filterValue); + }); + } } diff --git a/apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html b/apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html index 2d068dde1..91efd0972 100644 --- a/apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html +++ b/apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html @@ -42,12 +42,29 @@
Platform - - - {{ platform.name }} + + - + + + {{ platformEntry.name }} + + +
diff --git a/apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.module.ts b/apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.module.ts index 528835f9a..0f8b8ecb8 100644 --- a/apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.module.ts +++ b/apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.module.ts @@ -7,6 +7,8 @@ import { MatDialogModule } from '@angular/material/dialog'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; import { MatSelectModule } from '@angular/material/select'; +import { MatAutocompleteModule } from '@angular/material/autocomplete'; +import { GfSymbolIconModule } from '@ghostfolio/client/components/symbol-icon/symbol-icon.module'; import { CreateOrUpdateAccountDialog } from './create-or-update-account-dialog.component'; @@ -15,6 +17,8 @@ import { CreateOrUpdateAccountDialog } from './create-or-update-account-dialog.c imports: [ CommonModule, FormsModule, + GfSymbolIconModule, + MatAutocompleteModule, MatButtonModule, MatCheckboxModule, MatDialogModule, diff --git a/libs/common/src/lib/interfaces/info-item.interface.ts b/libs/common/src/lib/interfaces/info-item.interface.ts index 5ba295050..55ab7d06f 100644 --- a/libs/common/src/lib/interfaces/info-item.interface.ts +++ b/libs/common/src/lib/interfaces/info-item.interface.ts @@ -1,5 +1,5 @@ import { SubscriptionOffer } from '@ghostfolio/common/types'; -import { SymbolProfile, Tag } from '@prisma/client'; +import { Platform, SymbolProfile, Tag } from '@prisma/client'; import { Statistics } from './statistics.interface'; import { Subscription } from './subscription.interface'; @@ -13,7 +13,7 @@ export interface InfoItem { fearAndGreedDataSource?: string; globalPermissions: string[]; isReadOnlyMode?: boolean; - platforms: { id: string; name: string }[]; + platforms: Platform[]; statistics: Statistics; stripePublicKey?: string; subscriptions: { [offer in SubscriptionOffer]: Subscription };