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

310 lines
9 KiB
TypeScript
Raw Normal View History

import {
Component,
OnDestroy,
OnInit,
QueryList,
ViewChild,
ViewChildren,
} from '@angular/core'
2022-08-05 23:35:13 -07:00
import { ActivatedRoute, convertToParamMap, Router } from '@angular/router'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
2022-05-05 08:36:18 -07:00
import { filter, first, map, Subject, switchMap, takeUntil } from 'rxjs'
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
import { FilterRule } from 'src/app/data/filter-rule'
2022-10-26 12:48:22 -07:00
import {
filterRulesDiffer,
isFullTextFilterRule,
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
} from 'src/app/utils/filter-rules'
2022-05-05 08:36:18 -07:00
import { FILTER_FULLTEXT_MORELIKE } from 'src/app/data/filter-rule-type'
2023-12-19 22:36:35 -08:00
import { Document } from 'src/app/data/document'
import { SavedView } from 'src/app/data/saved-view'
import { SETTINGS_KEYS } from 'src/app/data/ui-settings'
import {
SortableDirective,
SortEvent,
} from 'src/app/directives/sortable.directive'
import { ConsumerStatusService } from 'src/app/services/consumer-status.service'
import { DocumentListViewService } from 'src/app/services/document-list-view.service'
import { OpenDocumentsService } from 'src/app/services/open-documents.service'
2022-05-05 08:36:18 -07:00
import {
DOCUMENT_SORT_FIELDS,
DOCUMENT_SORT_FIELDS_FULLTEXT,
} from 'src/app/services/rest/document.service'
import { SavedViewService } from 'src/app/services/rest/saved-view.service'
2023-03-17 16:36:08 -07:00
import { SettingsService } from 'src/app/services/settings.service'
import { ToastService } from 'src/app/services/toast.service'
import { ComponentWithPermissions } from '../with-permissions/with-permissions.component'
import { FilterEditorComponent } from './filter-editor/filter-editor.component'
import { SaveViewConfigDialogComponent } from './save-view-config-dialog/save-view-config-dialog.component'
2020-10-27 01:10:18 +01:00
@Component({
2023-09-14 14:03:28 -07:00
selector: 'pngx-document-list',
2020-10-27 01:10:18 +01:00
templateUrl: './document-list.component.html',
styleUrls: ['./document-list.component.scss'],
2020-10-27 01:10:18 +01:00
})
export class DocumentListComponent
extends ComponentWithPermissions
implements OnInit, OnDestroy
{
2020-10-27 01:10:18 +01:00
constructor(
public list: DocumentListViewService,
public savedViewService: SavedViewService,
2020-10-30 22:46:43 +01:00
public route: ActivatedRoute,
private router: Router,
private toastService: ToastService,
private modalService: NgbModal,
private consumerStatusService: ConsumerStatusService,
2023-03-17 16:36:08 -07:00
public openDocumentsService: OpenDocumentsService,
private settingsService: SettingsService
) {
super()
}
2020-10-27 01:10:18 +01:00
@ViewChild('filterEditor')
private filterEditor: FilterEditorComponent
2020-10-27 01:10:18 +01:00
@ViewChildren(SortableDirective) headers: QueryList<SortableDirective>
2020-10-27 01:10:18 +01:00
displayMode = 'smallCards' // largeCards, smallCards, details
2021-04-04 00:03:51 +02:00
unmodifiedFilterRules: FilterRule[] = []
2023-12-19 22:36:35 -08:00
private unmodifiedSavedView: SavedView
2021-01-06 07:57:33 -08:00
2022-03-27 23:41:29 -07:00
private unsubscribeNotifier: Subject<any> = new Subject()
2021-01-06 07:57:33 -08:00
2022-10-26 12:48:22 -07:00
get savedViewIsModified(): boolean {
if (!this.list.activeSavedViewId || !this.unmodifiedSavedView) return false
else {
return (
this.unmodifiedSavedView.sort_field !== this.list.sortField ||
this.unmodifiedSavedView.sort_reverse !== this.list.sortReverse ||
filterRulesDiffer(
this.unmodifiedSavedView.filter_rules,
this.list.filterRules
)
)
}
}
2021-01-06 10:57:13 -08:00
get isFiltered() {
return !!this.filterEditor?.rulesModified
2021-01-06 10:57:13 -08:00
}
getTitle() {
2022-10-26 12:48:22 -07:00
let title = this.list.activeSavedViewTitle
if (title && this.savedViewIsModified) {
title += '*'
} else if (!title) {
title = $localize`Documents`
}
return title
}
2020-10-27 01:10:18 +01:00
getSortFields() {
return isFullTextFilterRule(this.list.filterRules)
? DOCUMENT_SORT_FIELDS_FULLTEXT
: DOCUMENT_SORT_FIELDS
2020-10-27 01:10:18 +01:00
}
set listSortReverse(reverse: boolean) {
2022-05-05 08:36:18 -07:00
this.list.sortReverse = reverse
}
get listSortReverse(): boolean {
2022-05-05 08:36:18 -07:00
return this.list.sortReverse
}
setSortField(field: string) {
this.list.sortField = field
}
2021-01-04 17:08:52 +01:00
onSort(event: SortEvent) {
this.list.setSort(event.column, event.reverse)
2022-05-20 15:16:17 -07:00
}
2020-12-14 23:14:19 -08:00
get isBulkEditing(): boolean {
return this.list.selected.size > 0
}
2020-10-27 01:10:18 +01:00
saveDisplayMode() {
localStorage.setItem('document-list:displayMode', this.displayMode)
}
ngOnInit(): void {
if (localStorage.getItem('document-list:displayMode') != null) {
this.displayMode = localStorage.getItem('document-list:displayMode')
}
2022-03-26 23:18:12 -07:00
2022-03-28 00:06:42 -07:00
this.consumerStatusService
.onDocumentConsumptionFinished()
2022-03-27 23:41:29 -07:00
.pipe(takeUntil(this.unsubscribeNotifier))
.subscribe(() => {
this.list.reload()
})
2022-03-26 23:18:12 -07:00
this.route.paramMap
2022-03-27 23:41:29 -07:00
.pipe(
filter((params) => params.has('id')), // only on saved view e.g. /view/id
2022-03-27 23:41:29 -07:00
switchMap((params) => {
return this.savedViewService
.getCached(+params.get('id'))
.pipe(map((view) => ({ view })))
})
2022-03-27 23:41:29 -07:00
)
.pipe(takeUntil(this.unsubscribeNotifier))
.subscribe(({ view }) => {
2022-03-27 23:41:29 -07:00
if (!view) {
2023-08-08 23:59:13 -07:00
this.router.navigate(['404'], {
replaceUrl: true,
})
2022-03-27 23:41:29 -07:00
return
}
2022-10-26 12:48:22 -07:00
this.unmodifiedSavedView = view
2022-08-05 23:35:13 -07:00
this.list.activateSavedViewWithQueryParams(
view,
convertToParamMap(this.route.snapshot.queryParams)
)
2022-03-27 23:41:29 -07:00
this.list.reload()
this.unmodifiedFilterRules = view.filter_rules
2022-03-26 23:18:12 -07:00
})
this.route.queryParamMap
2022-03-27 23:41:29 -07:00
.pipe(
filter(() => !this.route.snapshot.paramMap.has('id')), // only when not on /view/id
2022-03-27 23:41:29 -07:00
takeUntil(this.unsubscribeNotifier)
)
2022-03-26 23:18:12 -07:00
.subscribe((queryParams) => {
2022-05-04 22:31:09 -07:00
if (queryParams.has('view')) {
// loading a saved view on /documents
this.loadViewConfig(parseInt(queryParams.get('view')))
2022-05-04 22:31:09 -07:00
} else {
this.list.activateSavedView(null)
2022-05-20 15:16:17 -07:00
this.list.loadFromQueryParams(queryParams)
2022-05-04 22:31:09 -07:00
this.unmodifiedFilterRules = []
}
2022-03-26 23:18:12 -07:00
})
}
ngOnDestroy() {
this.list.cancelPending()
2022-03-27 23:41:29 -07:00
this.unsubscribeNotifier.next(this)
this.unsubscribeNotifier.complete()
}
2020-10-30 22:46:43 +01:00
saveViewConfig() {
if (this.list.activeSavedViewId != null) {
2023-12-19 22:36:35 -08:00
let savedView: SavedView = {
id: this.list.activeSavedViewId,
filter_rules: this.list.filterRules,
sort_field: this.list.sortField,
sort_reverse: this.list.sortReverse,
}
2022-03-27 23:41:29 -07:00
this.savedViewService
.patch(savedView)
.pipe(first())
2022-10-26 12:48:22 -07:00
.subscribe((view) => {
this.unmodifiedSavedView = view
2022-03-27 23:41:29 -07:00
this.toastService.showInfo(
$localize`View "${this.list.activeSavedViewTitle}" saved successfully.`
)
this.unmodifiedFilterRules = this.list.filterRules
})
}
}
loadViewConfig(viewID: number) {
this.savedViewService
.getCached(viewID)
.pipe(first())
.subscribe((view) => {
2022-10-26 12:48:22 -07:00
this.unmodifiedSavedView = view
this.list.activateSavedView(view)
this.list.reload()
})
}
saveViewConfigAs() {
let modal = this.modalService.open(SaveViewConfigDialogComponent, {
backdrop: 'static',
})
modal.componentInstance.defaultName = this.filterEditor.generateFilterName()
2022-03-27 23:41:29 -07:00
modal.componentInstance.saveClicked.pipe(first()).subscribe((formValue) => {
2021-01-03 21:44:53 +01:00
modal.componentInstance.buttonsEnabled = false
2023-12-19 22:36:35 -08:00
let savedView: SavedView = {
name: formValue.name,
show_on_dashboard: formValue.showOnDashboard,
show_in_sidebar: formValue.showInSideBar,
filter_rules: this.list.filterRules,
sort_reverse: this.list.sortReverse,
sort_field: this.list.sortField,
2020-12-14 21:14:33 +01:00
}
2022-03-27 23:41:29 -07:00
this.savedViewService
.create(savedView)
.pipe(first())
.subscribe({
next: () => {
modal.close()
this.toastService.showInfo(
$localize`View "${savedView.name}" created successfully.`
)
},
error: (httpError) => {
let error = httpError.error
if (error.filter_rules) {
error.filter_rules = error.filter_rules.map((r) => r.value)
}
modal.componentInstance.error = error
2022-03-27 23:41:29 -07:00
modal.componentInstance.buttonsEnabled = true
},
})
2020-10-30 22:46:43 +01:00
})
}
2023-03-28 22:10:12 -07:00
2023-12-19 22:36:35 -08:00
openDocumentDetail(document: Document) {
2023-03-28 22:10:12 -07:00
this.router.navigate(['documents', document.id])
}
2023-12-19 22:36:35 -08:00
toggleSelected(document: Document, event: MouseEvent): void {
if (!event.shiftKey) this.list.toggleSelected(document)
else this.list.selectRangeTo(document)
}
clickTag(tagID: number) {
this.list.selectNone()
this.filterEditor.toggleTag(tagID)
}
clickCorrespondent(correspondentID: number) {
this.list.selectNone()
this.filterEditor.toggleCorrespondent(correspondentID)
}
clickDocumentType(documentTypeID: number) {
this.list.selectNone()
this.filterEditor.toggleDocumentType(documentTypeID)
}
Feature: Dynamic document storage pathes (#916) * Added devcontainer * Add feature storage pathes * Exclude tests and add versioning * Check escaping * Check escaping * Check quoting * Echo * Escape * Escape : * Double escape \ * Escaping * Remove if * Escape colon * Missing \ * Esacpe : * Escape all * test * Remove sed * Fix exclude * Remove SED command * Add LD_LIBRARY_PATH * Adjusted to v1.7 * Updated test-cases * Remove devcontainer * Removed internal build-file * Run pre-commit * Corrected flak8 error * Adjusted to v1.7 * Updated test-cases * Corrected flak8 error * Adjusted to new plural translations * Small adjustments due to code-review backend * Adjusted line-break * Removed PAPERLESS prefix from settings variables * Corrected style change due to search+replace * First documentation draft * Revert changes to Pipfile * Add sphinx-autobuild with keep-outdated * Revert merge error that results in wrong storage path is evaluated * Adjust styles of generated files ... * Adds additional testing to cover dynamic storage path functionality * Remove unnecessary condition * Add hint to edit storage path dialog * Correct spelling of pathes to paths * Minor documentation tweaks * Minor typo * improving wrapping of filter editor buttons with new storage path button * Update .gitignore * Fix select border radius in non input-groups * Better storage path edit hint * Add note to edit storage path dialog re document_renamer * Add note to bulk edit storage path re document_renamer * Rename FILTER_STORAGE_DIRECTORY to PATH * Fix broken filter rule parsing * Show default storage if unspecified * Remove note re storage path on bulk edit * Add basic validation of filename variables Co-authored-by: Markus Kling <markus@markus-kling.net> Co-authored-by: Trenton Holmes <holmes.trenton@gmail.com> Co-authored-by: Michael Shamoon <4887959+shamoon@users.noreply.github.com> Co-authored-by: Quinn Casey <quinn@quinncasey.com>
2022-05-19 23:42:25 +02:00
clickStoragePath(storagePathID: number) {
this.list.selectNone()
this.filterEditor.toggleStoragePath(storagePathID)
Feature: Dynamic document storage pathes (#916) * Added devcontainer * Add feature storage pathes * Exclude tests and add versioning * Check escaping * Check escaping * Check quoting * Echo * Escape * Escape : * Double escape \ * Escaping * Remove if * Escape colon * Missing \ * Esacpe : * Escape all * test * Remove sed * Fix exclude * Remove SED command * Add LD_LIBRARY_PATH * Adjusted to v1.7 * Updated test-cases * Remove devcontainer * Removed internal build-file * Run pre-commit * Corrected flak8 error * Adjusted to v1.7 * Updated test-cases * Corrected flak8 error * Adjusted to new plural translations * Small adjustments due to code-review backend * Adjusted line-break * Removed PAPERLESS prefix from settings variables * Corrected style change due to search+replace * First documentation draft * Revert changes to Pipfile * Add sphinx-autobuild with keep-outdated * Revert merge error that results in wrong storage path is evaluated * Adjust styles of generated files ... * Adds additional testing to cover dynamic storage path functionality * Remove unnecessary condition * Add hint to edit storage path dialog * Correct spelling of pathes to paths * Minor documentation tweaks * Minor typo * improving wrapping of filter editor buttons with new storage path button * Update .gitignore * Fix select border radius in non input-groups * Better storage path edit hint * Add note to edit storage path dialog re document_renamer * Add note to bulk edit storage path re document_renamer * Rename FILTER_STORAGE_DIRECTORY to PATH * Fix broken filter rule parsing * Show default storage if unspecified * Remove note re storage path on bulk edit * Add basic validation of filename variables Co-authored-by: Markus Kling <markus@markus-kling.net> Co-authored-by: Trenton Holmes <holmes.trenton@gmail.com> Co-authored-by: Michael Shamoon <4887959+shamoon@users.noreply.github.com> Co-authored-by: Quinn Casey <quinn@quinncasey.com>
2022-05-19 23:42:25 +02:00
}
clickMoreLike(documentID: number) {
2022-05-20 15:16:17 -07:00
this.list.quickFilter([
{ rule_type: FILTER_FULLTEXT_MORELIKE, value: documentID.toString() },
])
}
2023-12-19 22:36:35 -08:00
trackByDocumentId(index, item: Document) {
2020-12-22 02:59:09 +01:00
return item.id
}
2023-03-17 16:36:08 -07:00
get notesEnabled(): boolean {
return this.settingsService.get(SETTINGS_KEYS.NOTES_ENABLED)
}
resetFilters() {
this.filterEditor.resetSelected()
}
2020-10-27 01:10:18 +01:00
}