mirror of
				https://github.com/paperless-ngx/paperless-ngx.git
				synced 2025-10-30 03:56:23 -05:00 
			
		
		
		
	Merge branch 'dev' into ui-perms-tweaks
This commit is contained in:
		| @@ -2,7 +2,7 @@ | ||||
|   <label class="form-label" for="tags" i18n>Tags</label> | ||||
|  | ||||
|   <div class="input-group flex-nowrap"> | ||||
|     <ng-select name="tags" [items]="tags" bindLabel="name" bindValue="id" [(ngModel)]="value" | ||||
|     <ng-select #tagSelect name="tags" [items]="tags" bindLabel="name" bindValue="id" [(ngModel)]="value" | ||||
|       [disabled]="disabled" | ||||
|       [multiple]="true" | ||||
|       [closeOnSelect]="false" | ||||
| @@ -11,11 +11,7 @@ | ||||
|       [addTag]="allowCreate ? createTagRef : false" | ||||
|       addTagText="Add tag" | ||||
|       i18n-addTagText | ||||
|       (change)="onChange(value)" | ||||
|       (search)="onSearch($event)" | ||||
|       (focus)="clearLastSearchTerm()" | ||||
|       (clear)="clearLastSearchTerm()" | ||||
|       (blur)="onBlur()"> | ||||
|       (change)="onChange(value)"> | ||||
|  | ||||
|       <ng-template ng-label-tmp let-item="item"> | ||||
|         <span class="tag-wrap tag-wrap-delete" (mousedown)="removeTag($event, item.id)"> | ||||
|   | ||||
| @@ -15,16 +15,28 @@ import { | ||||
|   DEFAULT_MATCHING_ALGORITHM, | ||||
|   MATCH_ALL, | ||||
| } from 'src/app/data/matching-model' | ||||
| import { NgSelectModule } from '@ng-select/ng-select' | ||||
| import { NgSelectComponent, NgSelectModule } from '@ng-select/ng-select' | ||||
| import { RouterTestingModule } from '@angular/router/testing' | ||||
| import { HttpClientTestingModule } from '@angular/common/http/testing' | ||||
| import { of } from 'rxjs' | ||||
| import { TagService } from 'src/app/services/rest/tag.service' | ||||
| import { | ||||
|   NgbAccordionModule, | ||||
|   NgbModal, | ||||
|   NgbModalModule, | ||||
|   NgbModalRef, | ||||
|   NgbPopoverModule, | ||||
| } from '@ng-bootstrap/ng-bootstrap' | ||||
| import { TagEditDialogComponent } from '../../edit-dialog/tag-edit-dialog/tag-edit-dialog.component' | ||||
| import { CheckComponent } from '../check/check.component' | ||||
| import { IfOwnerDirective } from 'src/app/directives/if-owner.directive' | ||||
| import { TextComponent } from '../text/text.component' | ||||
| import { ColorComponent } from '../color/color.component' | ||||
| import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive' | ||||
| import { PermissionsFormComponent } from '../permissions/permissions-form/permissions-form.component' | ||||
| import { SelectComponent } from '../select/select.component' | ||||
| import { ColorSliderModule } from 'ngx-color/slider' | ||||
| import { By } from '@angular/platform-browser' | ||||
|  | ||||
| const tags: PaperlessTag[] = [ | ||||
|   { | ||||
| @@ -56,12 +68,32 @@ describe('TagsComponent', () => { | ||||
|  | ||||
|   beforeEach(async () => { | ||||
|     TestBed.configureTestingModule({ | ||||
|       declarations: [TagsComponent], | ||||
|       declarations: [ | ||||
|         TagsComponent, | ||||
|         TagEditDialogComponent, | ||||
|         TextComponent, | ||||
|         ColorComponent, | ||||
|         IfOwnerDirective, | ||||
|         SelectComponent, | ||||
|         TextComponent, | ||||
|         PermissionsFormComponent, | ||||
|         ColorComponent, | ||||
|         CheckComponent, | ||||
|       ], | ||||
|       providers: [ | ||||
|         { | ||||
|           provide: TagService, | ||||
|           useValue: { | ||||
|             listAll: () => of(tags), | ||||
|             listAll: () => | ||||
|               of({ | ||||
|                 results: tags, | ||||
|               }), | ||||
|             create: () => | ||||
|               of({ | ||||
|                 name: 'bar', | ||||
|                 id: 99, | ||||
|                 color: '#fff000', | ||||
|               }), | ||||
|           }, | ||||
|         }, | ||||
|       ], | ||||
| @@ -72,6 +104,8 @@ describe('TagsComponent', () => { | ||||
|         RouterTestingModule, | ||||
|         HttpClientTestingModule, | ||||
|         NgbModalModule, | ||||
|         NgbAccordionModule, | ||||
|         NgbPopoverModule, | ||||
|       ], | ||||
|     }).compileComponents() | ||||
|  | ||||
| @@ -85,7 +119,7 @@ describe('TagsComponent', () => { | ||||
|   }) | ||||
|  | ||||
|   it('should support suggestions', () => { | ||||
|     expect(component.value).toBeUndefined() | ||||
|     expect(component.value).toHaveLength(0) | ||||
|     component.value = [] | ||||
|     component.tags = tags | ||||
|     component.suggestions = [1, 2] | ||||
| @@ -107,19 +141,19 @@ describe('TagsComponent', () => { | ||||
|   it('should support create new using last search term and open a modal', () => { | ||||
|     let activeInstances: NgbModalRef[] | ||||
|     modalService.activeInstances.subscribe((v) => (activeInstances = v)) | ||||
|     component.onSearch({ term: 'bar' }) | ||||
|     component.select.searchTerm = 'foobar' | ||||
|     component.createTag() | ||||
|     expect(modalService.hasOpenModals()).toBeTruthy() | ||||
|     expect(activeInstances[0].componentInstance.object.name).toEqual('bar') | ||||
|     expect(activeInstances[0].componentInstance.object.name).toEqual('foobar') | ||||
|     const editDialog = activeInstances[0] | ||||
|       .componentInstance as TagEditDialogComponent | ||||
|     editDialog.save() // create is mocked | ||||
|     fixture.detectChanges() | ||||
|     fixture.whenStable().then(() => { | ||||
|       expect(fixture.debugElement.nativeElement.textContent).toContain('foobar') | ||||
|     }) | ||||
|   }) | ||||
|  | ||||
|   it('should clear search term on blur after delay', fakeAsync(() => { | ||||
|     const clearSpy = jest.spyOn(component, 'clearLastSearchTerm') | ||||
|     component.onBlur() | ||||
|     tick(3000) | ||||
|     expect(clearSpy).toHaveBeenCalled() | ||||
|   })) | ||||
|  | ||||
|   it('support remove tags', () => { | ||||
|     component.tags = tags | ||||
|     component.value = [1, 2] | ||||
| @@ -132,6 +166,7 @@ describe('TagsComponent', () => { | ||||
|   }) | ||||
|  | ||||
|   it('should get tags', () => { | ||||
|     component.tags = null | ||||
|     expect(component.getTag(2)).toBeNull() | ||||
|     component.tags = tags | ||||
|     expect(component.getTag(2)).toEqual(tags[1]) | ||||
|   | ||||
| @@ -5,6 +5,7 @@ import { | ||||
|   Input, | ||||
|   OnInit, | ||||
|   Output, | ||||
|   ViewChild, | ||||
| } from '@angular/core' | ||||
| import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms' | ||||
| import { NgbModal } from '@ng-bootstrap/ng-bootstrap' | ||||
| @@ -12,6 +13,8 @@ import { PaperlessTag } from 'src/app/data/paperless-tag' | ||||
| import { TagEditDialogComponent } from '../../edit-dialog/tag-edit-dialog/tag-edit-dialog.component' | ||||
| import { TagService } from 'src/app/services/rest/tag.service' | ||||
| import { EditDialogMode } from '../../edit-dialog/edit-dialog.component' | ||||
| import { first, firstValueFrom, tap } from 'rxjs' | ||||
| import { NgSelectComponent } from '@ng-select/ng-select' | ||||
|  | ||||
| @Component({ | ||||
|   providers: [ | ||||
| @@ -74,14 +77,14 @@ export class TagsComponent implements OnInit, ControlValueAccessor { | ||||
|   @Output() | ||||
|   filterDocuments = new EventEmitter<PaperlessTag[]>() | ||||
|  | ||||
|   value: number[] | ||||
|   @ViewChild('tagSelect') select: NgSelectComponent | ||||
|  | ||||
|   tags: PaperlessTag[] | ||||
|   value: number[] = [] | ||||
|  | ||||
|   tags: PaperlessTag[] = [] | ||||
|  | ||||
|   public createTagRef: (name) => void | ||||
|  | ||||
|   private _lastSearchTerm: string | ||||
|  | ||||
|   getTag(id: number) { | ||||
|     if (this.tags) { | ||||
|       return this.tags.find((tag) => tag.id == id) | ||||
| @@ -111,15 +114,20 @@ export class TagsComponent implements OnInit, ControlValueAccessor { | ||||
|     }) | ||||
|     modal.componentInstance.dialogMode = EditDialogMode.CREATE | ||||
|     if (name) modal.componentInstance.object = { name: name } | ||||
|     else if (this._lastSearchTerm) | ||||
|       modal.componentInstance.object = { name: this._lastSearchTerm } | ||||
|     modal.componentInstance.succeeded.subscribe((newTag) => { | ||||
|       this.tagService.listAll().subscribe((tags) => { | ||||
|         this.tags = tags.results | ||||
|         this.value = [...this.value, newTag.id] | ||||
|         this.onChange(this.value) | ||||
|       }) | ||||
|     }) | ||||
|     else if (this.select.searchTerm) | ||||
|       modal.componentInstance.object = { name: this.select.searchTerm } | ||||
|     this.select.searchTerm = null | ||||
|     this.select.detectChanges() | ||||
|     return firstValueFrom( | ||||
|       (modal.componentInstance as TagEditDialogComponent).succeeded.pipe( | ||||
|         first(), | ||||
|         tap(() => { | ||||
|           this.tagService.listAll().subscribe((tags) => { | ||||
|             this.tags = tags.results | ||||
|           }) | ||||
|         }) | ||||
|       ) | ||||
|     ) | ||||
|   } | ||||
|  | ||||
|   getSuggestions() { | ||||
| @@ -137,20 +145,6 @@ export class TagsComponent implements OnInit, ControlValueAccessor { | ||||
|     this.onChange(this.value) | ||||
|   } | ||||
|  | ||||
|   clearLastSearchTerm() { | ||||
|     this._lastSearchTerm = null | ||||
|   } | ||||
|  | ||||
|   onSearch($event) { | ||||
|     this._lastSearchTerm = $event.term | ||||
|   } | ||||
|  | ||||
|   onBlur() { | ||||
|     setTimeout(() => { | ||||
|       this.clearLastSearchTerm() | ||||
|     }, 3000) | ||||
|   } | ||||
|  | ||||
|   get hasPrivate(): boolean { | ||||
|     return this.value.some( | ||||
|       (t) => this.tags?.find((t2) => t2.id === t) === undefined | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 shamoon
					shamoon