paperless-ngx/src-ui/src/app/components/manage/management-list/management-list.component.ts

347 lines
9.8 KiB
TypeScript
Raw Normal View History

import {
Directive,
OnDestroy,
OnInit,
QueryList,
ViewChildren,
} from '@angular/core'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { Subject } from 'rxjs'
import { debounceTime, distinctUntilChanged, takeUntil } from 'rxjs/operators'
import {
MatchingModel,
MATCHING_ALGORITHMS,
MATCH_AUTO,
2023-02-21 20:01:30 -05:00
MATCH_NONE,
} from 'src/app/data/matching-model'
import { ObjectWithId } from 'src/app/data/object-with-id'
import { ObjectWithPermissions } from 'src/app/data/object-with-permissions'
import {
SortableDirective,
SortEvent,
} from 'src/app/directives/sortable.directive'
2022-05-20 15:16:17 -07:00
import { DocumentListViewService } from 'src/app/services/document-list-view.service'
import {
PermissionAction,
PermissionsService,
PermissionType,
} from 'src/app/services/permissions.service'
import {
AbstractNameFilterService,
BulkEditObjectOperation,
} from 'src/app/services/rest/abstract-name-filter-service'
import { ToastService } from 'src/app/services/toast.service'
import { ConfirmDialogComponent } from '../../common/confirm-dialog/confirm-dialog.component'
import { EditDialogMode } from '../../common/edit-dialog/edit-dialog.component'
import { ComponentWithPermissions } from '../../with-permissions/with-permissions.component'
import { PermissionsDialogComponent } from '../../common/permissions-dialog/permissions-dialog.component'
2020-10-27 01:10:18 +01:00
export interface ManagementListColumn {
key: string
name: string
valueFn: any
rendersHtml?: boolean
}
2020-10-27 01:10:18 +01:00
@Directive()
export abstract class ManagementListComponent<T extends ObjectWithId>
extends ComponentWithPermissions
implements OnInit, OnDestroy
{
2020-10-27 01:10:18 +01:00
constructor(
2021-01-19 14:00:15 +01:00
private service: AbstractNameFilterService<T>,
2020-10-27 01:10:18 +01:00
private modalService: NgbModal,
2021-01-03 21:44:53 +01:00
private editDialogComponent: any,
private toastService: ToastService,
2022-05-20 15:16:17 -07:00
private documentListViewService: DocumentListViewService,
private permissionsService: PermissionsService,
protected filterRuleType: number,
public typeName: string,
public typeNamePlural: string,
public permissionType: PermissionType,
public extraColumns: ManagementListColumn[]
) {
super()
}
2020-10-27 01:10:18 +01:00
@ViewChildren(SortableDirective) headers: QueryList<SortableDirective>
2020-11-08 16:58:06 +01:00
2020-10-27 01:10:18 +01:00
public data: T[] = []
public page = 1
public collectionSize = 0
2020-11-08 16:58:06 +01:00
public sortField: string
2021-01-04 15:58:26 +01:00
public sortReverse: boolean
2020-11-08 16:58:06 +01:00
public isLoading: boolean = false
2021-01-19 14:00:15 +01:00
private nameFilterDebounce: Subject<string>
private unsubscribeNotifier: Subject<any> = new Subject()
2021-01-19 14:00:15 +01:00
private _nameFilter: string
public selectedObjects: Set<number> = new Set()
2020-10-27 01:10:18 +01:00
ngOnInit(): void {
this.reloadData()
2021-01-19 14:00:15 +01:00
this.nameFilterDebounce = new Subject<string>()
this.nameFilterDebounce
.pipe(
takeUntil(this.unsubscribeNotifier),
debounceTime(400),
distinctUntilChanged()
)
.subscribe((title) => {
this._nameFilter = title
this.page = 1
this.reloadData()
})
2021-01-19 14:00:15 +01:00
}
ngOnDestroy() {
this.unsubscribeNotifier.next(true)
this.unsubscribeNotifier.complete()
2020-10-27 01:10:18 +01:00
}
getMatching(o: MatchingModel) {
if (o.matching_algorithm == MATCH_AUTO) {
return $localize`Automatic`
2023-02-21 20:01:30 -05:00
} else if (o.matching_algorithm == MATCH_NONE) {
return $localize`None`
} else if (o.match && o.match.length > 0) {
return `${
MATCHING_ALGORITHMS.find((a) => a.id == o.matching_algorithm).shortName
}: ${o.match}`
} else {
return '-'
}
}
onSort(event: SortEvent) {
this.sortField = event.column
this.sortReverse = event.reverse
this.reloadData()
}
2020-10-27 01:10:18 +01:00
reloadData() {
this.isLoading = true
this.service
.listFiltered(
this.page,
null,
this.sortField,
this.sortReverse,
this._nameFilter,
true
)
.pipe(takeUntil(this.unsubscribeNotifier))
.subscribe((c) => {
this.data = c.results
this.collectionSize = c.count
this.isLoading = false
})
2020-10-27 01:10:18 +01:00
}
openCreateDialog() {
var activeModal = this.modalService.open(this.editDialogComponent, {
backdrop: 'static',
})
frontend unit tests toasts component testing conditional import of angular setup-jest for vscode-jest support Update jest.config.js Create open-documents.service.spec.ts Add unit tests for all REST services settings service test Remove component from settings service test Create permissions.service.spec.ts upload documents service tests Update package.json Create toast.service.spec.ts Tasks service test Statistics widget component tests Update permissions.service.ts Create app.component.spec.ts settings component testing tasks component unit testing Management list component generic tests Some management component tests document notes component unit tests Create document-list.component.spec.ts Create save-view-config-dialog.component.spec.ts Create filter-editor.component.spec.ts small and large document cards unit testing Create bulk-editor.component.spec.ts document detail unit tests saving work on documentdetail component spec Create document-asn.component.spec.ts dashboard & widgets unit testing Fix ResizeObserver mock common component unit tests fix some merge errors Update app-frame.component.spec.ts Create page-header.component.spec.ts input component unit tests FilterableDropdownComponent unit testing and found minor errors update taskservice unit tests Edit dialogs unit tests Create date-dropdown.component.spec.ts Remove selectors from guard tests confirm dialog component tests app frame component test Miscellaneous component tests Update document-list-view.service.spec.ts directives unit tests Remove unused resizeobserver mock guard unit tests Update query-params.spec.ts try to fix flaky playwright filter rules utils & testing Interceptor unit tests Pipes unit testing Utils unit tests Update upload-documents.service.spec.ts consumer status service tests Update setup-jest.ts Create document-list-view.service.spec.ts Update app-routing.module.ts
2023-05-23 15:02:54 -07:00
activeModal.componentInstance.dialogMode = EditDialogMode.CREATE
2023-05-17 12:46:04 -07:00
activeModal.componentInstance.succeeded.subscribe(() => {
this.reloadData()
this.toastService.showInfo(
$localize`Successfully created ${this.typeName}.`
)
})
activeModal.componentInstance.failed.subscribe((e) => {
this.toastService.showError(
$localize`Error occurred while creating ${this.typeName}.`,
2023-08-23 23:50:54 -07:00
e
2023-05-17 12:46:04 -07:00
)
2020-10-27 01:10:18 +01:00
})
}
openEditDialog(object: T) {
var activeModal = this.modalService.open(this.editDialogComponent, {
backdrop: 'static',
})
2020-10-27 01:10:18 +01:00
activeModal.componentInstance.object = object
frontend unit tests toasts component testing conditional import of angular setup-jest for vscode-jest support Update jest.config.js Create open-documents.service.spec.ts Add unit tests for all REST services settings service test Remove component from settings service test Create permissions.service.spec.ts upload documents service tests Update package.json Create toast.service.spec.ts Tasks service test Statistics widget component tests Update permissions.service.ts Create app.component.spec.ts settings component testing tasks component unit testing Management list component generic tests Some management component tests document notes component unit tests Create document-list.component.spec.ts Create save-view-config-dialog.component.spec.ts Create filter-editor.component.spec.ts small and large document cards unit testing Create bulk-editor.component.spec.ts document detail unit tests saving work on documentdetail component spec Create document-asn.component.spec.ts dashboard & widgets unit testing Fix ResizeObserver mock common component unit tests fix some merge errors Update app-frame.component.spec.ts Create page-header.component.spec.ts input component unit tests FilterableDropdownComponent unit testing and found minor errors update taskservice unit tests Edit dialogs unit tests Create date-dropdown.component.spec.ts Remove selectors from guard tests confirm dialog component tests app frame component test Miscellaneous component tests Update document-list-view.service.spec.ts directives unit tests Remove unused resizeobserver mock guard unit tests Update query-params.spec.ts try to fix flaky playwright filter rules utils & testing Interceptor unit tests Pipes unit testing Utils unit tests Update upload-documents.service.spec.ts consumer status service tests Update setup-jest.ts Create document-list-view.service.spec.ts Update app-routing.module.ts
2023-05-23 15:02:54 -07:00
activeModal.componentInstance.dialogMode = EditDialogMode.EDIT
2023-05-17 12:46:04 -07:00
activeModal.componentInstance.succeeded.subscribe(() => {
this.reloadData()
this.toastService.showInfo(
$localize`Successfully updated ${this.typeName}.`
)
})
activeModal.componentInstance.failed.subscribe((e) => {
this.toastService.showError(
$localize`Error occurred while saving ${this.typeName}.`,
2023-08-23 23:50:54 -07:00
e
2023-05-17 12:46:04 -07:00
)
2020-10-27 01:10:18 +01:00
})
}
frontend unit tests toasts component testing conditional import of angular setup-jest for vscode-jest support Update jest.config.js Create open-documents.service.spec.ts Add unit tests for all REST services settings service test Remove component from settings service test Create permissions.service.spec.ts upload documents service tests Update package.json Create toast.service.spec.ts Tasks service test Statistics widget component tests Update permissions.service.ts Create app.component.spec.ts settings component testing tasks component unit testing Management list component generic tests Some management component tests document notes component unit tests Create document-list.component.spec.ts Create save-view-config-dialog.component.spec.ts Create filter-editor.component.spec.ts small and large document cards unit testing Create bulk-editor.component.spec.ts document detail unit tests saving work on documentdetail component spec Create document-asn.component.spec.ts dashboard & widgets unit testing Fix ResizeObserver mock common component unit tests fix some merge errors Update app-frame.component.spec.ts Create page-header.component.spec.ts input component unit tests FilterableDropdownComponent unit testing and found minor errors update taskservice unit tests Edit dialogs unit tests Create date-dropdown.component.spec.ts Remove selectors from guard tests confirm dialog component tests app frame component test Miscellaneous component tests Update document-list-view.service.spec.ts directives unit tests Remove unused resizeobserver mock guard unit tests Update query-params.spec.ts try to fix flaky playwright filter rules utils & testing Interceptor unit tests Pipes unit testing Utils unit tests Update upload-documents.service.spec.ts consumer status service tests Update setup-jest.ts Create document-list-view.service.spec.ts Update app-routing.module.ts
2023-05-23 15:02:54 -07:00
abstract getDeleteMessage(object: T)
filterDocuments(object: ObjectWithId) {
2022-05-20 15:16:17 -07:00
this.documentListViewService.quickFilter([
{ rule_type: this.filterRuleType, value: object.id.toString() },
])
2020-10-27 17:33:57 +01:00
}
openDeleteDialog(object: T) {
var activeModal = this.modalService.open(ConfirmDialogComponent, {
backdrop: 'static',
})
activeModal.componentInstance.title = $localize`Confirm delete`
activeModal.componentInstance.messageBold = this.getDeleteMessage(object)
activeModal.componentInstance.message = $localize`Associated documents will not be deleted.`
activeModal.componentInstance.btnClass = 'btn-danger'
activeModal.componentInstance.btnCaption = $localize`Delete`
activeModal.componentInstance.confirmClicked.subscribe(() => {
activeModal.componentInstance.buttonsEnabled = false
this.service
.delete(object)
.pipe(takeUntil(this.unsubscribeNotifier))
.subscribe({
next: () => {
activeModal.close()
this.reloadData()
},
error: (error) => {
activeModal.componentInstance.buttonsEnabled = true
this.toastService.showError(
$localize`Error while deleting element`,
error
)
},
})
})
2020-10-27 01:10:18 +01:00
}
2021-01-19 14:00:15 +01:00
get nameFilter() {
return this._nameFilter
}
set nameFilter(nameFilter: string) {
this.nameFilterDebounce.next(nameFilter)
}
onNameFilterKeyUp(event: KeyboardEvent) {
if (event.code == 'Escape') this.nameFilterDebounce.next(null)
}
userCanDelete(object: ObjectWithPermissions): boolean {
return this.permissionsService.currentUserOwnsObject(object)
}
userCanEdit(object: ObjectWithPermissions): boolean {
return this.permissionsService.currentUserHasObjectPermissions(
this.PermissionAction.Change,
object
)
}
userCanBulkEdit(action: PermissionAction): boolean {
if (!this.permissionsService.currentUserCan(action, this.permissionType))
return false
let ownsAll: boolean = true
const objects = this.data.filter((o) => this.selectedObjects.has(o.id))
ownsAll = objects.every((o) =>
this.permissionsService.currentUserOwnsObject(o)
)
return ownsAll
}
toggleAll(event: PointerEvent) {
if ((event.target as HTMLInputElement).checked) {
this.selectedObjects = new Set(this.data.map((o) => o.id))
} else {
this.clearSelection()
}
}
clearSelection() {
this.selectedObjects.clear()
}
toggleSelected(object) {
this.selectedObjects.has(object.id)
? this.selectedObjects.delete(object.id)
: this.selectedObjects.add(object.id)
}
setPermissions() {
let modal = this.modalService.open(PermissionsDialogComponent, {
backdrop: 'static',
})
modal.componentInstance.confirmClicked.subscribe(
({ permissions, merge }) => {
modal.componentInstance.buttonsEnabled = false
this.service
.bulk_edit_objects(
Array.from(this.selectedObjects),
BulkEditObjectOperation.SetPermissions,
permissions,
merge
)
.subscribe({
next: () => {
modal.close()
this.toastService.showInfo(
$localize`Permissions updated successfully`
)
this.reloadData()
},
error: (error) => {
modal.componentInstance.buttonsEnabled = true
this.toastService.showError(
$localize`Error updating permissions`,
error
)
},
})
}
)
}
delete() {
let modal = this.modalService.open(ConfirmDialogComponent, {
backdrop: 'static',
})
modal.componentInstance.title = $localize`Confirm delete`
modal.componentInstance.messageBold = $localize`This operation will permanently delete all objects.`
modal.componentInstance.message = $localize`This operation cannot be undone.`
modal.componentInstance.btnClass = 'btn-danger'
modal.componentInstance.btnCaption = $localize`Proceed`
modal.componentInstance.confirmClicked.subscribe(() => {
modal.componentInstance.buttonsEnabled = false
this.service
.bulk_edit_objects(
Array.from(this.selectedObjects),
BulkEditObjectOperation.Delete
)
.subscribe({
next: () => {
modal.close()
this.toastService.showInfo($localize`Objects deleted successfully`)
this.reloadData()
},
error: (error) => {
modal.componentInstance.buttonsEnabled = true
this.toastService.showError(
$localize`Error deleting objects`,
error
)
},
})
})
}
2020-10-27 01:10:18 +01:00
}