2022-03-11 10:53:32 -08:00
|
|
|
import {
|
|
|
|
|
Directive,
|
|
|
|
|
OnDestroy,
|
|
|
|
|
OnInit,
|
|
|
|
|
QueryList,
|
|
|
|
|
ViewChildren,
|
|
|
|
|
} from '@angular/core'
|
|
|
|
|
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
2023-09-19 13:40:21 -07:00
|
|
|
import { Subject } from 'rxjs'
|
2023-08-22 22:11:53 -07:00
|
|
|
import { debounceTime, distinctUntilChanged, takeUntil } from 'rxjs/operators'
|
2022-03-11 10:53:32 -08:00
|
|
|
import {
|
|
|
|
|
MatchingModel,
|
|
|
|
|
MATCHING_ALGORITHMS,
|
|
|
|
|
MATCH_AUTO,
|
2023-02-21 20:01:30 -05:00
|
|
|
MATCH_NONE,
|
2022-03-11 10:53:32 -08:00
|
|
|
} from 'src/app/data/matching-model'
|
|
|
|
|
import { ObjectWithId } from 'src/app/data/object-with-id'
|
2024-02-25 16:59:59 -08:00
|
|
|
import { ObjectWithPermissions } from 'src/app/data/object-with-permissions'
|
2022-03-11 10:53:32 -08:00
|
|
|
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'
|
2022-12-07 17:49:02 -08:00
|
|
|
import {
|
2024-02-25 16:59:59 -08:00
|
|
|
PermissionAction,
|
2022-12-07 17:49:02 -08:00
|
|
|
PermissionsService,
|
|
|
|
|
PermissionType,
|
|
|
|
|
} from 'src/app/services/permissions.service'
|
2024-02-08 10:13:15 -08:00
|
|
|
import {
|
|
|
|
|
AbstractNameFilterService,
|
|
|
|
|
BulkEditObjectOperation,
|
|
|
|
|
} from 'src/app/services/rest/abstract-name-filter-service'
|
2022-03-11 10:53:32 -08:00
|
|
|
import { ToastService } from 'src/app/services/toast.service'
|
|
|
|
|
import { ConfirmDialogComponent } from '../../common/confirm-dialog/confirm-dialog.component'
|
2023-09-19 13:40:21 -07:00
|
|
|
import { EditDialogMode } from '../../common/edit-dialog/edit-dialog.component'
|
2022-11-12 04:03:35 -08:00
|
|
|
import { ComponentWithPermissions } from '../../with-permissions/with-permissions.component'
|
2023-09-19 13:40:21 -07:00
|
|
|
import { PermissionsDialogComponent } from '../../common/permissions-dialog/permissions-dialog.component'
|
2020-10-27 01:10:18 +01:00
|
|
|
|
2022-03-22 22:01:46 -07:00
|
|
|
export interface ManagementListColumn {
|
|
|
|
|
key: string
|
|
|
|
|
|
|
|
|
|
name: string
|
|
|
|
|
|
|
|
|
|
valueFn: any
|
|
|
|
|
|
|
|
|
|
rendersHtml?: boolean
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-27 01:10:18 +01:00
|
|
|
@Directive()
|
2022-03-22 22:01:46 -07:00
|
|
|
export abstract class ManagementListComponent<T extends ObjectWithId>
|
2022-11-12 04:03:35 -08:00
|
|
|
extends ComponentWithPermissions
|
2022-03-11 10:53:32 -08:00
|
|
|
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,
|
2022-03-22 22:01:46 -07:00
|
|
|
private toastService: ToastService,
|
2022-05-20 15:16:17 -07:00
|
|
|
private documentListViewService: DocumentListViewService,
|
2022-12-07 17:49:02 -08:00
|
|
|
private permissionsService: PermissionsService,
|
2022-03-22 22:01:46 -07:00
|
|
|
protected filterRuleType: number,
|
|
|
|
|
public typeName: string,
|
2022-05-12 20:34:32 -07:00
|
|
|
public typeNamePlural: string,
|
2022-11-12 04:03:35 -08:00
|
|
|
public permissionType: PermissionType,
|
2022-03-22 22:01:46 -07:00
|
|
|
public extraColumns: ManagementListColumn[]
|
2022-11-12 04:03:35 -08:00
|
|
|
) {
|
|
|
|
|
super()
|
|
|
|
|
}
|
2020-10-27 01:10:18 +01:00
|
|
|
|
2022-03-11 10:53:32 -08: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
|
|
|
|
2023-08-22 22:11:53 -07:00
|
|
|
public isLoading: boolean = false
|
|
|
|
|
|
2021-01-19 14:00:15 +01:00
|
|
|
private nameFilterDebounce: Subject<string>
|
2023-08-22 22:11:53 -07:00
|
|
|
private unsubscribeNotifier: Subject<any> = new Subject()
|
2021-01-19 14:00:15 +01:00
|
|
|
private _nameFilter: string
|
|
|
|
|
|
2023-09-19 13:40:21 -07:00
|
|
|
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>()
|
|
|
|
|
|
2023-08-22 22:11:53 -07:00
|
|
|
this.nameFilterDebounce
|
|
|
|
|
.pipe(
|
|
|
|
|
takeUntil(this.unsubscribeNotifier),
|
|
|
|
|
debounceTime(400),
|
|
|
|
|
distinctUntilChanged()
|
|
|
|
|
)
|
2022-03-11 10:53:32 -08:00
|
|
|
.subscribe((title) => {
|
|
|
|
|
this._nameFilter = title
|
|
|
|
|
this.page = 1
|
|
|
|
|
this.reloadData()
|
|
|
|
|
})
|
2021-01-19 14:00:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ngOnDestroy() {
|
2023-08-22 22:11:53 -07:00
|
|
|
this.unsubscribeNotifier.next(true)
|
|
|
|
|
this.unsubscribeNotifier.complete()
|
2020-10-27 01:10:18 +01:00
|
|
|
}
|
|
|
|
|
|
2022-03-22 22:01:46 -07: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`
|
2022-03-22 22:01:46 -07:00
|
|
|
} 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() {
|
2023-08-22 22:11:53 -07:00
|
|
|
this.isLoading = true
|
2022-03-11 10:53:32 -08:00
|
|
|
this.service
|
|
|
|
|
.listFiltered(
|
|
|
|
|
this.page,
|
|
|
|
|
null,
|
|
|
|
|
this.sortField,
|
|
|
|
|
this.sortReverse,
|
2023-04-26 03:03:33 -07:00
|
|
|
this._nameFilter,
|
|
|
|
|
true
|
2022-03-11 10:53:32 -08:00
|
|
|
)
|
2023-08-22 22:11:53 -07:00
|
|
|
.pipe(takeUntil(this.unsubscribeNotifier))
|
2022-03-11 10:53:32 -08:00
|
|
|
.subscribe((c) => {
|
|
|
|
|
this.data = c.results
|
|
|
|
|
this.collectionSize = c.count
|
2023-08-22 22:11:53 -07:00
|
|
|
this.isLoading = false
|
2022-03-11 10:53:32 -08:00
|
|
|
})
|
2020-10-27 01:10:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
openCreateDialog() {
|
2022-03-11 10:53:32 -08:00
|
|
|
var activeModal = this.modalService.open(this.editDialogComponent, {
|
|
|
|
|
backdrop: 'static',
|
|
|
|
|
})
|
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) {
|
2022-03-11 10:53:32 -08:00
|
|
|
var activeModal = this.modalService.open(this.editDialogComponent, {
|
|
|
|
|
backdrop: 'static',
|
|
|
|
|
})
|
2020-10-27 01:10:18 +01:00
|
|
|
activeModal.componentInstance.object = object
|
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
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-23 15:02:54 -07:00
|
|
|
abstract getDeleteMessage(object: T)
|
2022-03-22 22:01:46 -07:00
|
|
|
|
|
|
|
|
filterDocuments(object: ObjectWithId) {
|
2022-05-20 15:16:17 -07:00
|
|
|
this.documentListViewService.quickFilter([
|
2022-03-22 22:01:46 -07:00
|
|
|
{ rule_type: this.filterRuleType, value: object.id.toString() },
|
|
|
|
|
])
|
2020-10-27 17:33:57 +01:00
|
|
|
}
|
|
|
|
|
|
2024-02-21 15:27:15 -08: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)
|
|
|
|
|
}
|
2022-03-11 10:53:32 -08:00
|
|
|
|
2022-03-07 09:45:58 -08:00
|
|
|
onNameFilterKeyUp(event: KeyboardEvent) {
|
|
|
|
|
if (event.code == 'Escape') this.nameFilterDebounce.next(null)
|
|
|
|
|
}
|
2022-12-07 17:49:02 -08:00
|
|
|
|
|
|
|
|
userCanDelete(object: ObjectWithPermissions): boolean {
|
2022-12-07 21:11:47 -08:00
|
|
|
return this.permissionsService.currentUserOwnsObject(object)
|
2022-12-07 17:49:02 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
userCanEdit(object: ObjectWithPermissions): boolean {
|
|
|
|
|
return this.permissionsService.currentUserHasObjectPermissions(
|
|
|
|
|
this.PermissionAction.Change,
|
|
|
|
|
object
|
|
|
|
|
)
|
|
|
|
|
}
|
2023-09-19 13:40:21 -07:00
|
|
|
|
2024-02-25 16:59:59 -08:00
|
|
|
userCanBulkEdit(action: PermissionAction): boolean {
|
|
|
|
|
if (!this.permissionsService.currentUserCan(action, this.permissionType))
|
|
|
|
|
return false
|
2023-09-19 13:40:21 -07:00
|
|
|
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(
|
2024-02-01 11:56:57 -08:00
|
|
|
({ permissions, merge }) => {
|
2023-09-19 13:40:21 -07:00
|
|
|
modal.componentInstance.buttonsEnabled = false
|
|
|
|
|
this.service
|
2024-02-08 10:13:15 -08:00
|
|
|
.bulk_edit_objects(
|
2023-09-19 13:40:21 -07:00
|
|
|
Array.from(this.selectedObjects),
|
2024-02-08 10:13:15 -08:00
|
|
|
BulkEditObjectOperation.SetPermissions,
|
2024-02-01 11:56:57 -08:00
|
|
|
permissions,
|
|
|
|
|
merge
|
2023-09-19 13:40:21 -07:00
|
|
|
)
|
|
|
|
|
.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
|
|
|
|
|
)
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
}
|
2024-02-08 10:13:15 -08:00
|
|
|
|
|
|
|
|
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
|
|
|
}
|