From 5bbe8ce80b38267b71bb25aa0c58b2ee53fa1bae Mon Sep 17 00:00:00 2001 From: Dan Percic Date: Sat, 21 Aug 2021 00:42:58 +0300 Subject: [PATCH] refactor input with action --- ...ttributes-csv-import-dialog.component.html | 3 +- .../dictionary-listing-screen.component.html | 3 +- ...r-attributes-listing-screen.component.html | 3 +- ...er-templates-listing-screen.component.html | 3 +- ...e-attributes-listing-screen.component.html | 3 +- .../user-listing-screen.component.html | 3 +- .../comments/comments.component.html | 4 +- .../components/comments/comments.component.ts | 45 +++++++--------- .../file-workload/file-workload.component.ts | 7 ++- .../page-exclusion.component.html | 4 +- .../page-exclusion.component.ts | 52 ++++++++----------- .../team-members-manager.component.html | 4 +- .../team-members-manager.component.ts | 19 +++---- .../search-screen/search-screen.component.ts | 5 +- .../annotation-icon.component.ts | 2 - .../input-with-action.component.html | 51 ++++++++---------- .../input-with-action.component.ts | 32 ++++++------ .../page-header/page-header.component.html | 3 +- apps/red-ui/src/app/services/user.service.ts | 8 +-- 19 files changed, 103 insertions(+), 151 deletions(-) diff --git a/apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/file-attributes-csv-import-dialog.component.html b/apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/file-attributes-csv-import-dialog.component.html index 142aee418..9187425af 100644 --- a/apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/file-attributes-csv-import-dialog.component.html +++ b/apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/file-attributes-csv-import-dialog.component.html @@ -90,9 +90,8 @@
diff --git a/apps/red-ui/src/app/modules/admin/screens/dictionary-listing/dictionary-listing-screen.component.html b/apps/red-ui/src/app/modules/admin/screens/dictionary-listing/dictionary-listing-screen.component.html index 8443f12e3..05df1370f 100644 --- a/apps/red-ui/src/app/modules/admin/screens/dictionary-listing/dictionary-listing-screen.component.html +++ b/apps/red-ui/src/app/modules/admin/screens/dictionary-listing/dictionary-listing-screen.component.html @@ -126,9 +126,8 @@
diff --git a/apps/red-ui/src/app/modules/admin/screens/user-listing/user-listing-screen.component.html b/apps/red-ui/src/app/modules/admin/screens/user-listing/user-listing-screen.component.html index 83537ffec..61ecdb6a4 100644 --- a/apps/red-ui/src/app/modules/admin/screens/user-listing/user-listing-screen.component.html +++ b/apps/red-ui/src/app/modules/admin/screens/user-listing/user-listing-screen.component.html @@ -9,9 +9,8 @@
diff --git a/apps/red-ui/src/app/modules/dossier/components/comments/comments.component.ts b/apps/red-ui/src/app/modules/dossier/components/comments/comments.component.ts index 006223e29..6b49b7fea 100644 --- a/apps/red-ui/src/app/modules/dossier/components/comments/comments.component.ts +++ b/apps/red-ui/src/app/modules/dossier/components/comments/comments.component.ts @@ -1,11 +1,8 @@ -import { ChangeDetectorRef, Component, HostBinding, Input } from '@angular/core'; -import { FormBuilder, FormGroup } from '@angular/forms'; +import { Component, HostBinding, Input } from '@angular/core'; import { Comment } from '@redaction/red-ui-http'; import { ManualAnnotationService } from '../../services/manual-annotation.service'; import { AnnotationWrapper } from '@models/file/annotation.wrapper'; import { UserService } from '@services/user.service'; -import { AppStateService } from '@state/app-state.service'; -import { TranslateService } from '@ngx-translate/core'; import { PermissionsService } from '@services/permissions.service'; @Component({ @@ -15,49 +12,43 @@ import { PermissionsService } from '@services/permissions.service'; }) export class CommentsComponent { @Input() annotation: AnnotationWrapper; - commentForm: FormGroup; @HostBinding('class.hidden') private _hidden = true; constructor( - readonly translateService: TranslateService, readonly permissionsService: PermissionsService, - private readonly _changeDetectorRef: ChangeDetectorRef, - private readonly _appStateService: AppStateService, - private readonly _formBuilder: FormBuilder, private readonly _userService: UserService, private readonly _manualAnnotationService: ManualAnnotationService - ) { - this.commentForm = this._formBuilder.group({ - value: [''] - }); - } + ) {} - addComment(): void { - const value = this.commentForm.value.value; - if (value) { - this._manualAnnotationService.addComment(value, this.annotation.id).subscribe(commentResponse => { + addComment(value: string): void { + if (!value) return; + this._manualAnnotationService + .addComment(value, this.annotation.id) + .toPromise() + .then(commentResponse => { this.annotation.comments.push({ text: value, id: commentResponse.commentId, user: this._userService.currentUser.id }); }); - this.commentForm.reset(); - } } - toggleExpandComments($event?: MouseEvent) { + toggleExpandComments($event?: MouseEvent): void { $event?.stopPropagation(); this._hidden = !this._hidden; } deleteComment(comment: Comment): void { - this._manualAnnotationService.deleteComment(comment.id, this.annotation.id).subscribe(() => { - this.annotation.comments.splice(this.annotation.comments.indexOf(comment), 1); - if (!this.annotation.comments.length) { - this._hidden = true; - } - }); + this._manualAnnotationService + .deleteComment(comment.id, this.annotation.id) + .toPromise() + .then(() => { + this.annotation.comments.splice(this.annotation.comments.indexOf(comment), 1); + if (!this.annotation.comments.length) { + this._hidden = true; + } + }); } getOwnerName(comment: Comment): string { diff --git a/apps/red-ui/src/app/modules/dossier/components/file-workload/file-workload.component.ts b/apps/red-ui/src/app/modules/dossier/components/file-workload/file-workload.component.ts index 18aef5bbd..ed99585ad 100644 --- a/apps/red-ui/src/app/modules/dossier/components/file-workload/file-workload.component.ts +++ b/apps/red-ui/src/app/modules/dossier/components/file-workload/file-workload.component.ts @@ -26,6 +26,10 @@ import { map } from 'rxjs/operators'; const COMMAND_KEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Escape']; const ALL_HOTKEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown']; +interface Target extends EventTarget { + localName: string; +} + @Component({ selector: 'redaction-file-workload', templateUrl: './file-workload.component.html', @@ -155,6 +159,7 @@ export class FileWorkloadComponent { } annotationClicked(annotation: AnnotationWrapper, $event: MouseEvent): void { + if (($event.target as Target).localName === 'input') return; this.pagesPanelActive = false; this.logAnnotation(annotation); if (this.isSelected(annotation)) { @@ -175,7 +180,7 @@ export class FileWorkloadComponent { if ( !ALL_HOTKEY_ARRAY.includes($event.key) || this.dialogRef?.getState() === MatDialogState.OPEN || - ($event.target as any).localName === 'input' + ($event.target as Target).localName === 'input' ) { return; } diff --git a/apps/red-ui/src/app/modules/dossier/components/page-exclusion/page-exclusion.component.html b/apps/red-ui/src/app/modules/dossier/components/page-exclusion/page-exclusion.component.html index 3d0bc5b17..1194b3321 100644 --- a/apps/red-ui/src/app/modules/dossier/components/page-exclusion/page-exclusion.component.html +++ b/apps/red-ui/src/app/modules/dossier/components/page-exclusion/page-exclusion.component.html @@ -1,12 +1,10 @@
diff --git a/apps/red-ui/src/app/modules/dossier/components/page-exclusion/page-exclusion.component.ts b/apps/red-ui/src/app/modules/dossier/components/page-exclusion/page-exclusion.component.ts index c564e2b4c..5e5b9e1ed 100644 --- a/apps/red-ui/src/app/modules/dossier/components/page-exclusion/page-exclusion.component.ts +++ b/apps/red-ui/src/app/modules/dossier/components/page-exclusion/page-exclusion.component.ts @@ -1,11 +1,11 @@ -import { Component, EventEmitter, Input, OnChanges, Output } from '@angular/core'; +import { Component, EventEmitter, Input, OnChanges, Output, ViewChild } from '@angular/core'; import { PermissionsService } from '@services/permissions.service'; -import { FormBuilder, FormGroup } from '@angular/forms'; import { PageRange, ReanalysisControllerService } from '@redaction/red-ui-http'; import { Toaster } from '@iqser/common-ui'; import { LoadingService } from '@services/loading.service'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { FileStatusWrapper } from '@models/file/file-status.wrapper'; +import { InputWithActionComponent } from '@shared/components/input-with-action/input-with-action.component'; @Component({ selector: 'redaction-page-exclusion', @@ -14,24 +14,19 @@ import { FileStatusWrapper } from '@models/file/file-status.wrapper'; }) export class PageExclusionComponent implements OnChanges { @Input() fileStatus: FileStatusWrapper; - @Output() actionPerformed = new EventEmitter(); + @Output() readonly actionPerformed = new EventEmitter(); - excludePagesForm: FormGroup; excludedPagesRanges: PageRange[] = []; + @ViewChild(InputWithActionComponent) private readonly _inputComponent: InputWithActionComponent; constructor( readonly permissionsService: PermissionsService, - private readonly _formBuilder: FormBuilder, private readonly _reanalysisControllerService: ReanalysisControllerService, private readonly _toaster: Toaster, private readonly _loadingService: LoadingService - ) { - this.excludePagesForm = this._formBuilder.group({ - value: [''] - }); - } + ) {} - ngOnChanges() { + ngOnChanges(): void { const excludedPages = (this.fileStatus?.excludedPages || []).sort((p1, p2) => p1 - p2); this.excludedPagesRanges = excludedPages.reduce((ranges, page) => { if (!ranges.length) { @@ -47,24 +42,21 @@ export class PageExclusionComponent implements OnChanges { }, []); } - async excludePagesRange() { + async excludePagesRange(value: string): Promise { this._loadingService.start(); try { - const pageRanges = this.excludePagesForm - .get('value') - .value.split(',') - .map(range => { - const splitted = range.split('-'); - const startPage = parseInt(splitted[0], 10); - const endPage = splitted.length > 1 ? parseInt(splitted[1], 10) : startPage; - if (!startPage || !endPage) { - throw new Error(); - } - return { - startPage, - endPage - }; - }); + const pageRanges = value.split(',').map(range => { + const splitted = range.split('-'); + const startPage = parseInt(splitted[0], 10); + const endPage = splitted.length > 1 ? parseInt(splitted[1], 10) : startPage; + if (!startPage || !endPage) { + throw new Error(); + } + return { + startPage, + endPage + }; + }); await this._reanalysisControllerService .excludePages( { @@ -74,7 +66,7 @@ export class PageExclusionComponent implements OnChanges { this.fileStatus.fileId ) .toPromise(); - this.excludePagesForm.reset(); + this._inputComponent.reset(); this.actionPerformed.emit('exclude-pages'); } catch (e) { this._toaster.error(_('file-preview.tabs.exclude-pages.error')); @@ -82,7 +74,7 @@ export class PageExclusionComponent implements OnChanges { } } - async includePagesRange(range: PageRange) { + async includePagesRange(range: PageRange): Promise { this._loadingService.start(); await this._reanalysisControllerService .includePages( @@ -93,7 +85,7 @@ export class PageExclusionComponent implements OnChanges { this.fileStatus.fileId ) .toPromise(); - this.excludePagesForm.reset(); + this._inputComponent.reset(); this.actionPerformed.emit('exclude-pages'); } } diff --git a/apps/red-ui/src/app/modules/dossier/components/team-members-manager/team-members-manager.component.html b/apps/red-ui/src/app/modules/dossier/components/team-members-manager/team-members-manager.component.html index 8e0bd8f9e..9e573d614 100644 --- a/apps/red-ui/src/app/modules/dossier/components/team-members-manager/team-members-manager.component.html +++ b/apps/red-ui/src/app/modules/dossier/components/team-members-manager/team-members-manager.component.html @@ -36,11 +36,11 @@

 
     
 
     
diff --git a/apps/red-ui/src/app/modules/dossier/components/team-members-manager/team-members-manager.component.ts b/apps/red-ui/src/app/modules/dossier/components/team-members-manager/team-members-manager.component.ts index 2c6e89a78..d71cf0bdd 100644 --- a/apps/red-ui/src/app/modules/dossier/components/team-members-manager/team-members-manager.component.ts +++ b/apps/red-ui/src/app/modules/dossier/components/team-members-manager/team-members-manager.component.ts @@ -13,12 +13,12 @@ import { DossierWrapper } from '@state/model/dossier.wrapper'; }) export class TeamMembersManagerComponent implements OnInit { teamForm: FormGroup; - searchForm: FormGroup; + searchQuery = ''; @Input() dossierWrapper: DossierWrapper; - @Output() save = new EventEmitter(); - ownersSelectOptions: string[] = this.userService.managerUsers.map(m => m.id); + @Output() readonly save = new EventEmitter(); + readonly ownersSelectOptions = this.userService.managerUsers.map(m => m.id); selectedReviewersList: string[] = []; membersSelectOptions: string[] = []; changed = false; @@ -137,10 +137,9 @@ export class TeamMembersManagerComponent implements OnInit { this.selectedReviewersList = this.selectedMembersList.filter(m => this.selectedApproversList.indexOf(m) === -1); } - private _setMembersSelectOptions() { - const searchQuery = this.searchForm.get('query').value; + setMembersSelectOptions(): void { this.membersSelectOptions = this.userService.eligibleUsers - .filter(user => this.userService.getNameForId(user.id).toLowerCase().includes(searchQuery.toLowerCase())) + .filter(user => this.userService.getNameForId(user.id).toLowerCase().includes(this.searchQuery.toLowerCase())) .filter(user => this.selectedOwnerId !== user.id) .map(user => user.id); } @@ -151,9 +150,6 @@ export class TeamMembersManagerComponent implements OnInit { approvers: [[...this.dossierWrapper?.approverIds]], members: [[...this.dossierWrapper?.memberIds]] }); - this.searchForm = this._formBuilder.group({ - query: [''] - }); this.teamForm.get('owner').valueChanges.subscribe(owner => { if (!this.isApprover(owner)) { this.toggleApprover(owner); @@ -161,15 +157,12 @@ export class TeamMembersManagerComponent implements OnInit { // If it is an approver, it is already a member, no need to check this._updateLists(); }); - this.searchForm.get('query').valueChanges.subscribe(() => { - this._setMembersSelectOptions(); - }); this._updateLists(); } private _updateLists() { this._setSelectedReviewersList(); - this._setMembersSelectOptions(); + this.setMembersSelectOptions(); this._updateChanged(); } diff --git a/apps/red-ui/src/app/modules/dossier/screens/search-screen/search-screen.component.ts b/apps/red-ui/src/app/modules/dossier/screens/search-screen/search-screen.component.ts index b0c4a2b50..1651d0392 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/search-screen/search-screen.component.ts +++ b/apps/red-ui/src/app/modules/dossier/screens/search-screen/search-screen.component.ts @@ -85,10 +85,7 @@ export class SearchScreenComponent extends ListingComponent implements ) .subscribe(mappedValue => this._updateValues(mappedValue)); - this.addSubscription = this.searchService.searchForm - .get('query') - .valueChanges.pipe(debounceTime(300)) - .subscribe(value => this.updateNavigation(value)); + this.addSubscription = this.searchService.valueChanges$.pipe(debounceTime(300)).subscribe(value => this.updateNavigation(value)); this.addSubscription = this.filterService.filterGroups$.pipe(skip(1)).subscribe(group => { const dossierIds = group[0].filters.filter(v => v.checked).map(v => v.key); diff --git a/apps/red-ui/src/app/modules/shared/components/annotation-icon/annotation-icon.component.ts b/apps/red-ui/src/app/modules/shared/components/annotation-icon/annotation-icon.component.ts index b66f0a1be..f35c4566e 100644 --- a/apps/red-ui/src/app/modules/shared/components/annotation-icon/annotation-icon.component.ts +++ b/apps/red-ui/src/app/modules/shared/components/annotation-icon/annotation-icon.component.ts @@ -14,8 +14,6 @@ export class AnnotationIconComponent implements OnChanges { @ViewChild('icon', { static: true }) icon: ElementRef; - constructor() {} - get isHint() { return this.type === 'circle' || this.dictType?.type === 'hint'; } diff --git a/apps/red-ui/src/app/modules/shared/components/input-with-action/input-with-action.component.html b/apps/red-ui/src/app/modules/shared/components/input-with-action/input-with-action.component.html index 55f148d32..f05829b60 100644 --- a/apps/red-ui/src/app/modules/shared/components/input-with-action/input-with-action.component.html +++ b/apps/red-ui/src/app/modules/shared/components/input-with-action/input-with-action.component.html @@ -1,34 +1,25 @@ -
-
- +
+ - {{ hint }} + {{ hint }} - - + - + - - -
- + +
diff --git a/apps/red-ui/src/app/modules/shared/components/input-with-action/input-with-action.component.ts b/apps/red-ui/src/app/modules/shared/components/input-with-action/input-with-action.component.ts index cc00278b3..1193c4310 100644 --- a/apps/red-ui/src/app/modules/shared/components/input-with-action/input-with-action.component.ts +++ b/apps/red-ui/src/app/modules/shared/components/input-with-action/input-with-action.component.ts @@ -1,5 +1,4 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; -import { FormGroup } from '@angular/forms'; @Component({ selector: 'redaction-input-with-action', @@ -7,35 +6,36 @@ import { FormGroup } from '@angular/forms'; styleUrls: ['./input-with-action.component.scss'] }) export class InputWithActionComponent { - @Input() form: FormGroup; @Input() placeholder: string; @Input() hint: string; @Input() width: number | 'full' = 250; - @Input() type: 'search' | 'action'; @Input() icon: string; @Input() autocomplete: 'on' | 'off' = 'on'; - @Output() action = new EventEmitter(); + @Input() value = ''; + @Output() readonly action = new EventEmitter(); + @Output() readonly valueChange = new EventEmitter(); - get formControlName(): 'query' | 'value' { - return this.type === 'search' ? 'query' : 'value'; + get hasContent(): boolean { + return !!this.value.length; } - get hasContent() { - return !!this.form.get(this.formControlName).value?.length; - } - - get computedWidth() { + get computedWidth(): string { return this.width === 'full' ? '100%' : `${this.width}px`; } - clearContent() { - this.form.patchValue({ query: '' }, { emitEvent: true }); + reset(): void { + this.value = ''; } - executeAction($event?: MouseEvent) { - $event?.stopPropagation(); + get isSearch(): boolean { + return this.action.observers.length === 0; + } + + executeAction($event: MouseEvent): void { + $event.stopPropagation(); + if (this.hasContent) { - this.action.emit(); + this.action.emit(this.value); } } } diff --git a/apps/red-ui/src/app/modules/shared/components/page-header/page-header.component.html b/apps/red-ui/src/app/modules/shared/components/page-header/page-header.component.html index 486a71672..b041f0f17 100644 --- a/apps/red-ui/src/app/modules/shared/components/page-header/page-header.component.html +++ b/apps/red-ui/src/app/modules/shared/components/page-header/page-header.component.html @@ -53,11 +53,10 @@ diff --git a/apps/red-ui/src/app/services/user.service.ts b/apps/red-ui/src/app/services/user.service.ts index 8ab66e8c5..875d228e7 100644 --- a/apps/red-ui/src/app/services/user.service.ts +++ b/apps/red-ui/src/app/services/user.service.ts @@ -16,7 +16,7 @@ export interface ProfileModel { } export class UserWrapper { - constructor(private readonly _user: KeycloakProfile | User, public roles: string[], public id: string) {} + constructor(private readonly _user: KeycloakProfile | User, public roles: string[], readonly id: string) {} email = this._user.email; username = this._user.username || this.email; @@ -106,11 +106,7 @@ export class UserService { getNameForId(userId: string): string | undefined { const user = this.getUserById(userId); - return user ? this.getName(user) : undefined; - } - - getName({ firstName, lastName, username }: UserWrapper) { - return firstName && lastName ? `${firstName} ${lastName}` : username; + return user ? user.name : undefined; } isManager(user: UserWrapper = this._currentUser): boolean {