From 19184bb38203b8920b4d70f412159dce6085b8d3 Mon Sep 17 00:00:00 2001 From: Daniel Idem Date: Thu, 29 Aug 2024 17:08:59 +0100 Subject: [PATCH] Update Tags Selector and GfHoldingDetailDialogComponent --- .../holding-detail-dialog.component.ts | 60 ++-------- .../holding-detail-dialog.html | 37 +----- ...ate-or-update-activity-dialog.component.ts | 6 - .../tags-selector.component.html | 6 +- .../tags-selector/tags-selector.component.ts | 107 +++++------------- 5 files changed, 43 insertions(+), 173 deletions(-) diff --git a/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts b/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts index 64c062c7e..ccfa2b518 100644 --- a/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts +++ b/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts @@ -17,6 +17,7 @@ import { GfDataProviderCreditsComponent } from '@ghostfolio/ui/data-provider-cre import { translate } from '@ghostfolio/ui/i18n'; import { GfLineChartComponent } from '@ghostfolio/ui/line-chart'; import { GfPortfolioProportionChartComponent } from '@ghostfolio/ui/portfolio-proportion-chart'; +import { GfTagsSelectorComponent } from '@ghostfolio/ui/tags-selector'; import { GfValueComponent } from '@ghostfolio/ui/value'; import { COMMA, ENTER } from '@angular/cdk/keycodes'; @@ -26,19 +27,13 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, - ElementRef, Inject, OnDestroy, - OnInit, - ViewChild + OnInit } from '@angular/core'; import { FormBuilder, FormGroup } from '@angular/forms'; -import { - MatAutocompleteModule, - MatAutocompleteSelectedEvent -} from '@angular/material/autocomplete'; +import { MatAutocompleteModule } from '@angular/material/autocomplete'; import { MatButtonModule } from '@angular/material/button'; -import { MatChipsModule } from '@angular/material/chips'; import { MAT_DIALOG_DATA, MatDialogModule, @@ -52,8 +47,8 @@ import { Router } from '@angular/router'; import { Account, Tag } from '@prisma/client'; import { format, isSameMonth, isToday, parseISO } from 'date-fns'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; -import { Observable, of, Subject } from 'rxjs'; -import { map, startWith, takeUntil } from 'rxjs/operators'; +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; import { HoldingDetailDialogParams } from './interfaces/interfaces'; @@ -69,10 +64,10 @@ import { HoldingDetailDialogParams } from './interfaces/interfaces'; GfDialogHeaderModule, GfLineChartComponent, GfPortfolioProportionChartComponent, + GfTagsSelectorComponent, GfValueComponent, MatAutocompleteModule, MatButtonModule, - MatChipsModule, MatDialogModule, MatFormFieldModule, MatTabsModule, @@ -85,8 +80,6 @@ import { HoldingDetailDialogParams } from './interfaces/interfaces'; templateUrl: 'holding-detail-dialog.html' }) export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit { - @ViewChild('tagInput') tagInput: ElementRef; - public activityForm: FormGroup; public accounts: Account[]; public activities: Activity[]; @@ -103,7 +96,6 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit { public dividendInBaseCurrencyPrecision = 2; public dividendYieldPercentWithCurrencyEffect: number; public feeInBaseCurrency: number; - public filteredTagsObservable: Observable = of([]); public firstBuyDate: string; public historicalDataItems: LineChartItem[]; public investment: number; @@ -305,17 +297,6 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit { this.activityForm.setValue({ tags: this.tags }, { emitEvent: false }); - this.filteredTagsObservable = this.activityForm.controls[ - 'tags' - ].valueChanges.pipe( - startWith(this.activityForm.get('tags').value), - map((aTags: Tag[] | null) => { - return aTags - ? this.filterTags(aTags) - : this.tagsAvailable.slice(); - }) - ); - this.transactionCount = transactionCount; this.totalItems = transactionCount; this.value = value; @@ -415,15 +396,8 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit { }); } - public onAddTag(event: MatAutocompleteSelectedEvent) { - this.activityForm.get('tags').setValue([ - ...(this.activityForm.get('tags').value ?? []), - this.tagsAvailable.find(({ id }) => { - return id === event.option.value; - }) - ]); - - this.tagInput.nativeElement.value = ''; + public onAddTag(event: Tag[]) { + this.activityForm.get('tags').setValue(event); } public onCloneActivity(aActivity: Activity) { @@ -458,14 +432,6 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit { }); } - public onRemoveTag(aTag: Tag) { - this.activityForm.get('tags').setValue( - this.activityForm.get('tags').value.filter(({ id }) => { - return id !== aTag.id; - }) - ); - } - public onUpdateActivity(aActivity: Activity) { this.router.navigate(['/portfolio', 'activities'], { queryParams: { activityId: aActivity.id, editDialog: true } @@ -478,14 +444,4 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit { this.unsubscribeSubject.next(); this.unsubscribeSubject.complete(); } - - private filterTags(aTags: Tag[]) { - const tagIds = aTags.map(({ id }) => { - return id; - }); - - return this.tagsAvailable.filter(({ id }) => { - return !tagIds.includes(id); - }); - } } diff --git a/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html b/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html index fab5bc452..2fd686c5f 100644 --- a/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html +++ b/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html @@ -373,38 +373,11 @@ }" >
- - Tags - - @for (tag of activityForm.get('tags')?.value; track tag.id) { - - {{ tag.name }} - - - } - - - - @for (tag of filteredTagsObservable | async; track tag.id) { - - {{ tag.name }} - - } - - +
diff --git a/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.component.ts b/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.component.ts index b1d8a9dc8..a0413d590 100644 --- a/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.component.ts +++ b/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.component.ts @@ -140,12 +140,6 @@ export class CreateOrUpdateActivityDialog implements OnDestroy { updateAccountBalance: [false] }); - console.log( - 'Initialized Activity Form: ', - this.activityForm.get('tags').value - ); - console.log('Initialized Activity Tags Available: ', this.tagsAvailable); - this.activityForm.valueChanges .pipe( // Slightly delay until the more specific form control value changes have diff --git a/libs/ui/src/lib/tags-selector/tags-selector.component.html b/libs/ui/src/lib/tags-selector/tags-selector.component.html index 2b60801e6..3d55ced23 100644 --- a/libs/ui/src/lib/tags-selector/tags-selector.component.html +++ b/libs/ui/src/lib/tags-selector/tags-selector.component.html @@ -1,7 +1,7 @@ Tags - @for (tag of fruits(); track tag.id) { + @for (tag of tagsSignal(); track tag.id) { - - @for (tag of filteredFruits(); track tag.id) { + @for (tag of filteredTags(); track tag.id) { {{ tag.name }} diff --git a/libs/ui/src/lib/tags-selector/tags-selector.component.ts b/libs/ui/src/lib/tags-selector/tags-selector.component.ts index 9881d3de3..ae78e2247 100644 --- a/libs/ui/src/lib/tags-selector/tags-selector.component.ts +++ b/libs/ui/src/lib/tags-selector/tags-selector.component.ts @@ -1,12 +1,6 @@ -// import { CreateOrUpdateActivityDialogParams } from './interfaces/interfaces'; -import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface'; - -import { FocusMonitor } from '@angular/cdk/a11y'; -import { COMMA, ENTER } from '@angular/cdk/keycodes'; import { CommonModule } from '@angular/common'; import { ChangeDetectionStrategy, - ChangeDetectorRef, Component, computed, CUSTOM_ELEMENTS_SCHEMA, @@ -20,36 +14,19 @@ import { signal, ViewChild } from '@angular/core'; -import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { FormsModule } from '@angular/forms'; import { - MatAutocompleteTrigger, MatAutocompleteModule, MatAutocompleteSelectedEvent } from '@angular/material/autocomplete'; -import { - MatChipEditedEvent, - MatChipInputEvent, - MatChipsModule -} from '@angular/material/chips'; -import { MAT_DIALOG_DATA } from '@angular/material/dialog'; -import { - MatFormFieldControl, - MatFormFieldModule -} from '@angular/material/form-field'; +import { MatChipsModule } from '@angular/material/chips'; +import { MatFormFieldModule } from '@angular/material/form-field'; import { MatIconModule } from '@angular/material/icon'; -import { MatInput, MatInputModule } from '@angular/material/input'; +import { MatInputModule } from '@angular/material/input'; import { Tag } from '@prisma/client'; -import { map, Observable, of, startWith, Subject } from 'rxjs'; - -import { translate } from '../i18n'; -import { AbstractMatFormField } from '../shared/abstract-mat-form-field'; @Component({ changeDetection: ChangeDetectionStrategy.OnPush, - // host: { - // '[attr.aria-describedBy]': 'describedBy', - // '[id]': 'id' - // }, imports: [ CommonModule, FormsModule, @@ -57,15 +34,8 @@ import { AbstractMatFormField } from '../shared/abstract-mat-form-field'; MatFormFieldModule, MatInputModule, MatChipsModule, - MatIconModule, - ReactiveFormsModule + MatIconModule ], - // providers: [ - // { - // provide: MatFormFieldControl, - // useExisting: GfTagsSelectorComponent - // } - // ], schemas: [CUSTOM_ELEMENTS_SCHEMA], selector: 'gf-tags-selector', standalone: true, @@ -73,9 +43,6 @@ import { AbstractMatFormField } from '../shared/abstract-mat-form-field'; templateUrl: 'tags-selector.component.html' }) export class GfTagsSelectorComponent implements OnInit { - public focus(): void { - throw new Error('Method not implemented.'); - } @Input() tags: Tag[]; @Input() tagsAvailable: Tag[]; @@ -83,75 +50,57 @@ export class GfTagsSelectorComponent implements OnInit { @ViewChild('tagInput') tagInput: ElementRef; - // public activityForm: FormGroup; - public filteredTagsObservable: Observable = of([]); - public separatorKeysCodes: number[] = [COMMA, ENTER]; - - readonly fruits = signal([{ id: '', name: '' }]); - + readonly tagsSignal = signal([{ id: '', name: '' }]); readonly currentFruit = model(''); - - readonly filteredFruits = computed(() => { + readonly filteredTags = computed(() => { const currentFruit = this.currentFruit().toLowerCase(); - const aTags = this.tagsAvailable - ? this.tagsAvailable - : [{ id: '', name: '' }]; + const aTags = this.tagsAvailable ?? [{ id: '', name: '' }]; + const bTags = this.tagsSignal() ?? [{ id: '', name: '' }]; + const cTags = aTags.filter((value) => !bTags.includes(value)); + return currentFruit - ? aTags.filter((tag) => tag.name.toLowerCase().includes(currentFruit)) - : this.tagsAvailable; + ? cTags.filter((tag) => tag.name.toLowerCase().includes(currentFruit)) + : cTags; }); public constructor() { effect(() => { - if (this.fruits()) { - console.log('Emit Fruits: ', this.fruits()); - this.tagsChanged.emit(this.fruits()); + if (this.tagsSignal()) { + this.tagsChanged.emit(this.tagsSignal()); } }); } ngOnInit() { - this.fruits.set(this.tags); - - console.log('Tags Available : ', this.tagsAvailable); - console.log('Tags : ', this.tags); + this.tagsSignal.set(this.tags); } public onAddTag(event: MatAutocompleteSelectedEvent) { - if ( - this.fruits() && - this.fruits().some((el) => el.id === event.option.value) - ) { + const tagId = event.option.value; + const newTag = this.tagsAvailable.find(({ id }) => id === tagId); + + if (this.tagsSignal()?.some((el) => el.id === tagId)) { this.currentFruit.set(''); event.option.deselect(); return; } - this.fruits() - ? this.fruits.update((fruits) => [ - ...fruits, - this.tagsAvailable.find(({ id }) => { - return id === event.option.value; - }) - ]) - : this.fruits.update(() => [ - this.tagsAvailable.find(({ id }) => { - return id === event.option.value; - }) - ]); + this.tagsSignal() + ? this.tagsSignal.update((tags) => [...tags, newTag]) + : this.tagsSignal.update(() => [newTag]); this.currentFruit.set(''); event.option.deselect(); } public onRemoveTag(aTag: Tag) { - this.fruits.update((fruits) => { - const index = fruits.indexOf(aTag); + this.tagsSignal.update((tagsSignal) => { + const index = tagsSignal.indexOf(aTag); if (index < 0) { - return fruits; + return tagsSignal; } - fruits.splice(index, 1); - return [...fruits]; + tagsSignal.splice(index, 1); + return [...tagsSignal]; }); } }