2024-04-24 22:55:46 -07:00

229 lines
7.5 KiB
TypeScript

import {
Component,
ElementRef,
HostListener,
QueryList,
ViewChild,
ViewChildren,
} from '@angular/core'
import { Router } from '@angular/router'
import { NgbDropdown, NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'
import { Subject, debounceTime, distinctUntilChanged, filter } from 'rxjs'
import { ObjectWithId } from 'src/app/data/object-with-id'
import {
GlobalSearchResult,
SearchService,
} from 'src/app/services/rest/search.service'
import { CorrespondentEditDialogComponent } from '../../common/edit-dialog/correspondent-edit-dialog/correspondent-edit-dialog.component'
import { EditDialogMode } from '../../common/edit-dialog/edit-dialog.component'
import { DocumentTypeEditDialogComponent } from '../../common/edit-dialog/document-type-edit-dialog/document-type-edit-dialog.component'
import { StoragePathEditDialogComponent } from '../../common/edit-dialog/storage-path-edit-dialog/storage-path-edit-dialog.component'
import { TagEditDialogComponent } from '../../common/edit-dialog/tag-edit-dialog/tag-edit-dialog.component'
import { CustomFieldEditDialogComponent } from '../../common/edit-dialog/custom-field-edit-dialog/custom-field-edit-dialog.component'
import { GroupEditDialogComponent } from '../../common/edit-dialog/group-edit-dialog/group-edit-dialog.component'
import { MailAccountEditDialogComponent } from '../../common/edit-dialog/mail-account-edit-dialog/mail-account-edit-dialog.component'
import { MailRuleEditDialogComponent } from '../../common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component'
import { UserEditDialogComponent } from '../../common/edit-dialog/user-edit-dialog/user-edit-dialog.component'
import { WorkflowEditDialogComponent } from '../../common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component'
import { DocumentService } from 'src/app/services/rest/document.service'
import { DocumentListViewService } from 'src/app/services/document-list-view.service'
import {
FILTER_HAS_ANY_TAG,
FILTER_HAS_CORRESPONDENT_ANY,
FILTER_HAS_DOCUMENT_TYPE_ANY,
FILTER_HAS_STORAGE_PATH_ANY,
} from 'src/app/data/filter-rule-type'
@Component({
selector: 'pngx-global-search',
templateUrl: './global-search.component.html',
styleUrl: './global-search.component.scss',
})
export class GlobalSearchComponent {
public query: string
public queryDebounce: Subject<string>
public searchResults: GlobalSearchResult
private currentItemIndex: number
@ViewChild('searchInput') searchInput: ElementRef
@ViewChild('resultsDropdown') resultsDropdown: NgbDropdown
@ViewChildren('resultItem') resultItems: QueryList<ElementRef>
@HostListener('document:keydown', ['$event'])
handleKeyboardEvent(event: KeyboardEvent) {
if (event.key === 'k' && (event.ctrlKey || event.metaKey)) {
this.searchInput.nativeElement.focus()
}
if (this.searchResults && this.resultsDropdown.isOpen()) {
if (event.key === 'ArrowDown') {
if (this.currentItemIndex < this.resultItems.length - 1) {
this.currentItemIndex++
this.setCurrentItem()
event.preventDefault()
}
} else if (event.key === 'ArrowUp') {
if (this.currentItemIndex > 0) {
this.currentItemIndex--
this.setCurrentItem()
event.preventDefault()
}
} else if (event.key === 'Enter') {
this.resultItems.get(this.currentItemIndex).nativeElement.click()
}
}
}
constructor(
private searchService: SearchService,
private router: Router,
private modalService: NgbModal,
private documentService: DocumentService,
private documentListViewService: DocumentListViewService
) {
this.queryDebounce = new Subject<string>()
this.queryDebounce
.pipe(
debounceTime(400),
distinctUntilChanged(),
filter((query) => !query.length || query.length > 2)
)
.subscribe((text) => {
this.query = text
this.search(text)
})
}
private search(query: string) {
this.searchService.globalSearch(query).subscribe((results) => {
this.searchResults = results
this.resultsDropdown.open()
})
}
public primaryAction(type: string, object: ObjectWithId) {
this.reset(true)
let filterRuleType: number
let editDialogComponent: any
switch (type) {
case 'document':
this.router.navigate(['/documents', object.id])
return
case 'correspondent':
filterRuleType = FILTER_HAS_CORRESPONDENT_ANY
break
case 'documentType':
filterRuleType = FILTER_HAS_DOCUMENT_TYPE_ANY
break
case 'storagePath':
filterRuleType = FILTER_HAS_STORAGE_PATH_ANY
break
case 'tag':
filterRuleType = FILTER_HAS_ANY_TAG
break
case 'user':
editDialogComponent = UserEditDialogComponent
break
case 'group':
editDialogComponent = GroupEditDialogComponent
break
case 'mailAccount':
editDialogComponent = MailAccountEditDialogComponent
break
case 'mailRule':
editDialogComponent = MailRuleEditDialogComponent
break
case 'customField':
editDialogComponent = CustomFieldEditDialogComponent
break
case 'workflow':
editDialogComponent = WorkflowEditDialogComponent
break
}
if (filterRuleType) {
this.documentListViewService.quickFilter([
{ rule_type: filterRuleType, value: object.id.toString() },
])
} else if (editDialogComponent) {
const modalRef: NgbModalRef = this.modalService.open(
editDialogComponent,
{ size: 'lg' }
)
modalRef.componentInstance.dialogMode = EditDialogMode.EDIT
modalRef.componentInstance.object = object
}
}
public secondaryAction(type: string, object: ObjectWithId) {
this.reset(true)
let editDialogComponent: any
switch (type) {
case 'document':
this.router.navigate([this.documentService.getDownloadUrl(object.id)], {
skipLocationChange: true,
})
break
case 'correspondent':
editDialogComponent = CorrespondentEditDialogComponent
break
case 'documentType':
editDialogComponent = DocumentTypeEditDialogComponent
break
case 'storagePath':
editDialogComponent = StoragePathEditDialogComponent
break
case 'tag':
editDialogComponent = TagEditDialogComponent
break
}
if (editDialogComponent) {
const modalRef: NgbModalRef = this.modalService.open(
editDialogComponent,
{ size: 'lg' }
)
modalRef.componentInstance.dialogMode = EditDialogMode.EDIT
modalRef.componentInstance.object = object
}
}
private reset(close: boolean = false) {
this.queryDebounce.next('')
this.searchResults = null
this.currentItemIndex = undefined
if (close) {
this.resultsDropdown.close()
}
}
private setCurrentItem() {
const item: ElementRef = this.resultItems.get(this.currentItemIndex)
item.nativeElement.focus()
}
public searchInputKeyDown(event: KeyboardEvent) {
if (
event.key === 'ArrowDown' &&
this.searchResults &&
this.resultsDropdown.isOpen()
) {
this.currentItemIndex = 0
this.setCurrentItem()
} else if (
event.key === 'Enter' &&
this.searchResults?.total === 1 &&
this.resultsDropdown.isOpen()
) {
this.resultItems.first.nativeElement.click()
}
}
public onDropdownOpenChange(open: boolean) {
if (!open) {
this.reset()
}
}
}