mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-04-02 13:45:10 -05:00
Enhancement: file task filtering (#8421)
This commit is contained in:
parent
5f7c60d9a1
commit
b4e369b556
src-ui
messages.xlf
src/app/components/admin/tasks
@ -1880,6 +1880,10 @@
|
|||||||
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.html</context>
|
||||||
<context context-type="linenumber">36</context>
|
<context context-type="linenumber">36</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.ts</context>
|
||||||
|
<context context-type="linenumber">30</context>
|
||||||
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/trash/trash.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/trash/trash.component.html</context>
|
||||||
<context context-type="linenumber">35</context>
|
<context context-type="linenumber">35</context>
|
||||||
@ -2037,7 +2041,7 @@
|
|||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.ts</context>
|
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.ts</context>
|
||||||
<context context-type="linenumber">68</context>
|
<context context-type="linenumber">124</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="2134950584701094962" datatype="html">
|
<trans-unit id="2134950584701094962" datatype="html">
|
||||||
@ -2089,60 +2093,74 @@
|
|||||||
<context context-type="linenumber">147,149</context>
|
<context context-type="linenumber">147,149</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="4880728824338713664" datatype="html">
|
||||||
|
<source>Filter by</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.html</context>
|
||||||
|
<context context-type="linenumber">157</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="2525230676386818985" datatype="html">
|
||||||
|
<source>Result</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.ts</context>
|
||||||
|
<context context-type="linenumber">31</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="5404910960991552159" datatype="html">
|
<trans-unit id="5404910960991552159" datatype="html">
|
||||||
<source>Dismiss selected</source>
|
<source>Dismiss selected</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.ts</context>
|
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.ts</context>
|
||||||
<context context-type="linenumber">31</context>
|
<context context-type="linenumber">77</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="8829078752502782653" datatype="html">
|
<trans-unit id="8829078752502782653" datatype="html">
|
||||||
<source>Dismiss all</source>
|
<source>Dismiss all</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.ts</context>
|
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.ts</context>
|
||||||
<context context-type="linenumber">32</context>
|
<context context-type="linenumber">78</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="1323591410517879795" datatype="html">
|
<trans-unit id="1323591410517879795" datatype="html">
|
||||||
<source>Confirm Dismiss All</source>
|
<source>Confirm Dismiss All</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.ts</context>
|
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.ts</context>
|
||||||
<context context-type="linenumber">65</context>
|
<context context-type="linenumber">121</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="4157200209636243740" datatype="html">
|
<trans-unit id="4157200209636243740" datatype="html">
|
||||||
<source>Dismiss all <x id="PH" equiv-text="tasks.size"/> tasks?</source>
|
<source>Dismiss all <x id="PH" equiv-text="tasks.size"/> tasks?</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.ts</context>
|
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.ts</context>
|
||||||
<context context-type="linenumber">66</context>
|
<context context-type="linenumber">122</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="9011556615675272238" datatype="html">
|
<trans-unit id="9011556615675272238" datatype="html">
|
||||||
<source>queued</source>
|
<source>queued</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.ts</context>
|
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.ts</context>
|
||||||
<context context-type="linenumber">135</context>
|
<context context-type="linenumber">207</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="6415892379431855826" datatype="html">
|
<trans-unit id="6415892379431855826" datatype="html">
|
||||||
<source>started</source>
|
<source>started</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.ts</context>
|
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.ts</context>
|
||||||
<context context-type="linenumber">137</context>
|
<context context-type="linenumber">209</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="7510279840486540181" datatype="html">
|
<trans-unit id="7510279840486540181" datatype="html">
|
||||||
<source>completed</source>
|
<source>completed</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.ts</context>
|
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.ts</context>
|
||||||
<context context-type="linenumber">139</context>
|
<context context-type="linenumber">211</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="4083337005045748464" datatype="html">
|
<trans-unit id="4083337005045748464" datatype="html">
|
||||||
<source>failed</source>
|
<source>failed</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.ts</context>
|
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.ts</context>
|
||||||
<context context-type="linenumber">141</context>
|
<context context-type="linenumber">213</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="3418677553313974490" datatype="html">
|
<trans-unit id="3418677553313974490" datatype="html">
|
||||||
|
@ -118,13 +118,13 @@
|
|||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
<ul ngbNav #nav="ngbNav" [(activeId)]="activeTab" class="nav-tabs" (hidden)="duringTabChange($event)">
|
<ul ngbNav #nav="ngbNav" [(activeId)]="activeTab" class="nav-tabs" (hidden)="duringTabChange()" (navChange)="beforeTabChange()">
|
||||||
<li ngbNavItem="failed">
|
<li ngbNavItem="failed">
|
||||||
<a ngbNavLink i18n>Failed@if (tasksService.failedFileTasks.length > 0) {
|
<a ngbNavLink i18n>Failed@if (tasksService.failedFileTasks.length > 0) {
|
||||||
<span class="badge bg-danger ms-2">{{tasksService.failedFileTasks.length}}</span>
|
<span class="badge bg-danger ms-2">{{tasksService.failedFileTasks.length}}</span>
|
||||||
}</a>
|
}</a>
|
||||||
<ng-template ngbNavContent>
|
<ng-template ngbNavContent>
|
||||||
<ng-container [ngTemplateOutlet]="tasksTemplate" [ngTemplateOutletContext]="{tasks:tasksService.failedFileTasks}"></ng-container>
|
<ng-container [ngTemplateOutlet]="tasksTemplate" [ngTemplateOutletContext]="{tasks:currentTasks}"></ng-container>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</li>
|
</li>
|
||||||
<li ngbNavItem="completed">
|
<li ngbNavItem="completed">
|
||||||
@ -132,7 +132,7 @@
|
|||||||
<span class="badge bg-secondary ms-2">{{tasksService.completedFileTasks.length}}</span>
|
<span class="badge bg-secondary ms-2">{{tasksService.completedFileTasks.length}}</span>
|
||||||
}</a>
|
}</a>
|
||||||
<ng-template ngbNavContent>
|
<ng-template ngbNavContent>
|
||||||
<ng-container [ngTemplateOutlet]="tasksTemplate" [ngTemplateOutletContext]="{tasks:tasksService.completedFileTasks}"></ng-container>
|
<ng-container [ngTemplateOutlet]="tasksTemplate" [ngTemplateOutletContext]="{tasks:currentTasks}"></ng-container>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</li>
|
</li>
|
||||||
<li ngbNavItem="started">
|
<li ngbNavItem="started">
|
||||||
@ -140,7 +140,7 @@
|
|||||||
<span class="badge bg-secondary ms-2">{{tasksService.startedFileTasks.length}}</span>
|
<span class="badge bg-secondary ms-2">{{tasksService.startedFileTasks.length}}</span>
|
||||||
}</a>
|
}</a>
|
||||||
<ng-template ngbNavContent>
|
<ng-template ngbNavContent>
|
||||||
<ng-container [ngTemplateOutlet]="tasksTemplate" [ngTemplateOutletContext]="{tasks:tasksService.startedFileTasks}"></ng-container>
|
<ng-container [ngTemplateOutlet]="tasksTemplate" [ngTemplateOutletContext]="{tasks:currentTasks}"></ng-container>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</li>
|
</li>
|
||||||
<li ngbNavItem="queued">
|
<li ngbNavItem="queued">
|
||||||
@ -148,8 +148,35 @@
|
|||||||
<span class="badge bg-secondary ms-2">{{tasksService.queuedFileTasks.length}}</span>
|
<span class="badge bg-secondary ms-2">{{tasksService.queuedFileTasks.length}}</span>
|
||||||
}</a>
|
}</a>
|
||||||
<ng-template ngbNavContent>
|
<ng-template ngbNavContent>
|
||||||
<ng-container [ngTemplateOutlet]="tasksTemplate" [ngTemplateOutletContext]="{tasks:tasksService.queuedFileTasks}"></ng-container>
|
<ng-container [ngTemplateOutlet]="tasksTemplate" [ngTemplateOutletContext]="{tasks:currentTasks}"></ng-container>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="ms-auto">
|
||||||
|
<div class="form-inline d-flex align-items-center">
|
||||||
|
<div class="input-group input-group-sm flex-fill w-auto flex-nowrap">
|
||||||
|
<span class="input-group-text text-muted" i18n>Filter by</span>
|
||||||
|
@if (filterTargets.length > 1) {
|
||||||
|
<div ngbDropdown>
|
||||||
|
<button class="btn btn-sm btn-outline-primary" ngbDropdownToggle>{{filterTargetName}}</button>
|
||||||
|
<div class="dropdown-menu shadow" ngbDropdownMenu>
|
||||||
|
@for (t of filterTargets; track t.id) {
|
||||||
|
<button ngbDropdownItem [class.active]="filterTargetID === t.id" (click)="filterTargetID = t.id">{{t.name}}</button>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
} @else {
|
||||||
|
<span class="input-group-text">{{filterTargetName}}</span>
|
||||||
|
}
|
||||||
|
@if (filterText?.length) {
|
||||||
|
<button class="btn btn-link btn-sm px-2 position-absolute top-0 end-0 z-10" (click)="resetFilter()">
|
||||||
|
<i-bs width="1em" height="1em" name="x"></i-bs>
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
<input #filterInput class="form-control form-control-sm" type="text"
|
||||||
|
(keyup)="filterInputKeyup($event)"
|
||||||
|
[(ngModel)]="filterText">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div [ngbNavOutlet]="nav"></div>
|
<div [ngbNavOutlet]="nav"></div>
|
||||||
|
@ -26,3 +26,14 @@ pre {
|
|||||||
max-width: 150px;
|
max-width: 150px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.input-group .dropdown .btn {
|
||||||
|
border-top-right-radius: 0;
|
||||||
|
border-bottom-right-radius: 0;
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.z-10 {
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
@ -26,7 +26,7 @@ import { TasksService } from 'src/app/services/tasks.service'
|
|||||||
import { environment } from 'src/environments/environment'
|
import { environment } from 'src/environments/environment'
|
||||||
import { ConfirmDialogComponent } from '../../common/confirm-dialog/confirm-dialog.component'
|
import { ConfirmDialogComponent } from '../../common/confirm-dialog/confirm-dialog.component'
|
||||||
import { PageHeaderComponent } from '../../common/page-header/page-header.component'
|
import { PageHeaderComponent } from '../../common/page-header/page-header.component'
|
||||||
import { TasksComponent } from './tasks.component'
|
import { TasksComponent, TaskTab } from './tasks.component'
|
||||||
import { PermissionsGuard } from 'src/app/guards/permissions.guard'
|
import { PermissionsGuard } from 'src/app/guards/permissions.guard'
|
||||||
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
|
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
|
||||||
import { FormsModule } from '@angular/forms'
|
import { FormsModule } from '@angular/forms'
|
||||||
@ -167,7 +167,7 @@ describe('TasksComponent', () => {
|
|||||||
let currentTasksLength = tasks.filter(
|
let currentTasksLength = tasks.filter(
|
||||||
(t) => t.status === PaperlessTaskStatus.Failed
|
(t) => t.status === PaperlessTaskStatus.Failed
|
||||||
).length
|
).length
|
||||||
component.activeTab = 'failed'
|
component.activeTab = TaskTab.Failed
|
||||||
fixture.detectChanges()
|
fixture.detectChanges()
|
||||||
expect(tabButtons[0].nativeElement.textContent).toEqual(
|
expect(tabButtons[0].nativeElement.textContent).toEqual(
|
||||||
`Failed${currentTasksLength}`
|
`Failed${currentTasksLength}`
|
||||||
@ -179,7 +179,7 @@ describe('TasksComponent', () => {
|
|||||||
currentTasksLength = tasks.filter(
|
currentTasksLength = tasks.filter(
|
||||||
(t) => t.status === PaperlessTaskStatus.Complete
|
(t) => t.status === PaperlessTaskStatus.Complete
|
||||||
).length
|
).length
|
||||||
component.activeTab = 'completed'
|
component.activeTab = TaskTab.Completed
|
||||||
fixture.detectChanges()
|
fixture.detectChanges()
|
||||||
expect(tabButtons[1].nativeElement.textContent).toEqual(
|
expect(tabButtons[1].nativeElement.textContent).toEqual(
|
||||||
`Complete${currentTasksLength}`
|
`Complete${currentTasksLength}`
|
||||||
@ -188,7 +188,7 @@ describe('TasksComponent', () => {
|
|||||||
currentTasksLength = tasks.filter(
|
currentTasksLength = tasks.filter(
|
||||||
(t) => t.status === PaperlessTaskStatus.Started
|
(t) => t.status === PaperlessTaskStatus.Started
|
||||||
).length
|
).length
|
||||||
component.activeTab = 'started'
|
component.activeTab = TaskTab.Started
|
||||||
fixture.detectChanges()
|
fixture.detectChanges()
|
||||||
expect(tabButtons[2].nativeElement.textContent).toEqual(
|
expect(tabButtons[2].nativeElement.textContent).toEqual(
|
||||||
`Started${currentTasksLength}`
|
`Started${currentTasksLength}`
|
||||||
@ -197,7 +197,7 @@ describe('TasksComponent', () => {
|
|||||||
currentTasksLength = tasks.filter(
|
currentTasksLength = tasks.filter(
|
||||||
(t) => t.status === PaperlessTaskStatus.Pending
|
(t) => t.status === PaperlessTaskStatus.Pending
|
||||||
).length
|
).length
|
||||||
component.activeTab = 'queued'
|
component.activeTab = TaskTab.Queued
|
||||||
fixture.detectChanges()
|
fixture.detectChanges()
|
||||||
expect(tabButtons[3].nativeElement.textContent).toEqual(
|
expect(tabButtons[3].nativeElement.textContent).toEqual(
|
||||||
`Queued${currentTasksLength}`
|
`Queued${currentTasksLength}`
|
||||||
@ -206,7 +206,7 @@ describe('TasksComponent', () => {
|
|||||||
|
|
||||||
it('should to go page 1 between tab switch', () => {
|
it('should to go page 1 between tab switch', () => {
|
||||||
component.page = 10
|
component.page = 10
|
||||||
component.duringTabChange(2)
|
component.duringTabChange()
|
||||||
expect(component.page).toEqual(1)
|
expect(component.page).toEqual(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -289,4 +289,53 @@ describe('TasksComponent', () => {
|
|||||||
jest.advanceTimersByTime(6000)
|
jest.advanceTimersByTime(6000)
|
||||||
expect(reloadSpy).toHaveBeenCalledTimes(2)
|
expect(reloadSpy).toHaveBeenCalledTimes(2)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should filter tasks by file name', () => {
|
||||||
|
const input = fixture.debugElement.query(By.css('ul input[type=text]'))
|
||||||
|
input.nativeElement.value = '191092'
|
||||||
|
input.nativeElement.dispatchEvent(new Event('input'))
|
||||||
|
jest.advanceTimersByTime(150) // debounce time
|
||||||
|
fixture.detectChanges()
|
||||||
|
expect(component.filterText).toEqual('191092')
|
||||||
|
expect(
|
||||||
|
fixture.debugElement.queryAll(By.css('table tbody tr')).length
|
||||||
|
).toEqual(2) // 1 task x 2 lines
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should filter tasks by result', () => {
|
||||||
|
component.activeTab = TaskTab.Failed
|
||||||
|
fixture.detectChanges()
|
||||||
|
component.filterTargetID = 1
|
||||||
|
const input = fixture.debugElement.query(By.css('ul input[type=text]'))
|
||||||
|
input.nativeElement.value = 'duplicate'
|
||||||
|
input.nativeElement.dispatchEvent(new Event('input'))
|
||||||
|
jest.advanceTimersByTime(150) // debounce time
|
||||||
|
fixture.detectChanges()
|
||||||
|
expect(component.filterText).toEqual('duplicate')
|
||||||
|
expect(
|
||||||
|
fixture.debugElement.queryAll(By.css('table tbody tr')).length
|
||||||
|
).toEqual(4) // 2 tasks x 2 lines
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should support keyboard events for filtering', () => {
|
||||||
|
const input = fixture.debugElement.query(By.css('ul input[type=text]'))
|
||||||
|
input.nativeElement.value = '191092'
|
||||||
|
input.nativeElement.dispatchEvent(
|
||||||
|
new KeyboardEvent('keyup', { key: 'Enter' })
|
||||||
|
)
|
||||||
|
expect(component.filterText).toEqual('191092') // no debounce needed
|
||||||
|
input.nativeElement.dispatchEvent(
|
||||||
|
new KeyboardEvent('keyup', { key: 'Escape' })
|
||||||
|
)
|
||||||
|
expect(component.filterText).toEqual('')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should reset filter and target on tab switch', () => {
|
||||||
|
component.filterText = '191092'
|
||||||
|
component.filterTargetID = 1
|
||||||
|
component.activeTab = TaskTab.Completed
|
||||||
|
component.beforeTabChange()
|
||||||
|
expect(component.filterText).toEqual('')
|
||||||
|
expect(component.filterTargetID).toEqual(0)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
@ -1,12 +1,36 @@
|
|||||||
import { Component, OnInit, OnDestroy } from '@angular/core'
|
import { Component, OnInit, OnDestroy } from '@angular/core'
|
||||||
import { Router } from '@angular/router'
|
import { Router } from '@angular/router'
|
||||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { first } from 'rxjs'
|
import {
|
||||||
|
debounceTime,
|
||||||
|
distinctUntilChanged,
|
||||||
|
filter,
|
||||||
|
first,
|
||||||
|
Subject,
|
||||||
|
takeUntil,
|
||||||
|
} from 'rxjs'
|
||||||
import { PaperlessTask } from 'src/app/data/paperless-task'
|
import { PaperlessTask } from 'src/app/data/paperless-task'
|
||||||
import { TasksService } from 'src/app/services/tasks.service'
|
import { TasksService } from 'src/app/services/tasks.service'
|
||||||
import { ConfirmDialogComponent } from '../../common/confirm-dialog/confirm-dialog.component'
|
import { ConfirmDialogComponent } from '../../common/confirm-dialog/confirm-dialog.component'
|
||||||
import { ComponentWithPermissions } from '../../with-permissions/with-permissions.component'
|
import { ComponentWithPermissions } from '../../with-permissions/with-permissions.component'
|
||||||
|
|
||||||
|
export enum TaskTab {
|
||||||
|
Queued = 'queued',
|
||||||
|
Started = 'started',
|
||||||
|
Completed = 'completed',
|
||||||
|
Failed = 'failed',
|
||||||
|
}
|
||||||
|
|
||||||
|
enum TaskFilterTargetID {
|
||||||
|
Name,
|
||||||
|
Result,
|
||||||
|
}
|
||||||
|
|
||||||
|
const FILTER_TARGETS = [
|
||||||
|
{ id: TaskFilterTargetID.Name, name: $localize`Name` },
|
||||||
|
{ id: TaskFilterTargetID.Result, name: $localize`Result` },
|
||||||
|
]
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'pngx-tasks',
|
selector: 'pngx-tasks',
|
||||||
templateUrl: './tasks.component.html',
|
templateUrl: './tasks.component.html',
|
||||||
@ -16,7 +40,7 @@ export class TasksComponent
|
|||||||
extends ComponentWithPermissions
|
extends ComponentWithPermissions
|
||||||
implements OnInit, OnDestroy
|
implements OnInit, OnDestroy
|
||||||
{
|
{
|
||||||
public activeTab: string
|
public activeTab: TaskTab
|
||||||
public selectedTasks: Set<number> = new Set()
|
public selectedTasks: Set<number> = new Set()
|
||||||
public togggleAll: boolean = false
|
public togggleAll: boolean = false
|
||||||
public expandedTask: number
|
public expandedTask: number
|
||||||
@ -26,6 +50,28 @@ export class TasksComponent
|
|||||||
|
|
||||||
public autoRefreshInterval: any
|
public autoRefreshInterval: any
|
||||||
|
|
||||||
|
private _filterText: string = ''
|
||||||
|
get filterText() {
|
||||||
|
return this._filterText
|
||||||
|
}
|
||||||
|
set filterText(value: string) {
|
||||||
|
this.filterDebounce.next(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
public filterTargetID: TaskFilterTargetID = TaskFilterTargetID.Name
|
||||||
|
public get filterTargetName(): string {
|
||||||
|
return this.filterTargets.find((t) => t.id == this.filterTargetID).name
|
||||||
|
}
|
||||||
|
private filterDebounce: Subject<string> = new Subject<string>()
|
||||||
|
|
||||||
|
public get filterTargets(): Array<{ id: number; name: string }> {
|
||||||
|
return [TaskTab.Failed, TaskTab.Completed].includes(this.activeTab)
|
||||||
|
? FILTER_TARGETS
|
||||||
|
: FILTER_TARGETS.slice(0, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
private unsubscribeNotifier: Subject<any> = new Subject()
|
||||||
|
|
||||||
get dismissButtonText(): string {
|
get dismissButtonText(): string {
|
||||||
return this.selectedTasks.size > 0
|
return this.selectedTasks.size > 0
|
||||||
? $localize`Dismiss selected`
|
? $localize`Dismiss selected`
|
||||||
@ -43,11 +89,21 @@ export class TasksComponent
|
|||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.tasksService.reload()
|
this.tasksService.reload()
|
||||||
this.toggleAutoRefresh()
|
this.toggleAutoRefresh()
|
||||||
|
|
||||||
|
this.filterDebounce
|
||||||
|
.pipe(
|
||||||
|
takeUntil(this.unsubscribeNotifier),
|
||||||
|
debounceTime(100),
|
||||||
|
distinctUntilChanged(),
|
||||||
|
filter((query) => !query.length || query.length > 2)
|
||||||
|
)
|
||||||
|
.subscribe((query) => (this._filterText = query))
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
this.tasksService.cancelPending()
|
this.tasksService.cancelPending()
|
||||||
clearInterval(this.autoRefreshInterval)
|
clearInterval(this.autoRefreshInterval)
|
||||||
|
this.unsubscribeNotifier.next(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
dismissTask(task: PaperlessTask) {
|
dismissTask(task: PaperlessTask) {
|
||||||
@ -96,19 +152,30 @@ export class TasksComponent
|
|||||||
get currentTasks(): PaperlessTask[] {
|
get currentTasks(): PaperlessTask[] {
|
||||||
let tasks: PaperlessTask[] = []
|
let tasks: PaperlessTask[] = []
|
||||||
switch (this.activeTab) {
|
switch (this.activeTab) {
|
||||||
case 'queued':
|
case TaskTab.Queued:
|
||||||
tasks = this.tasksService.queuedFileTasks
|
tasks = this.tasksService.queuedFileTasks
|
||||||
break
|
break
|
||||||
case 'started':
|
case TaskTab.Started:
|
||||||
tasks = this.tasksService.startedFileTasks
|
tasks = this.tasksService.startedFileTasks
|
||||||
break
|
break
|
||||||
case 'completed':
|
case TaskTab.Completed:
|
||||||
tasks = this.tasksService.completedFileTasks
|
tasks = this.tasksService.completedFileTasks
|
||||||
break
|
break
|
||||||
case 'failed':
|
case TaskTab.Failed:
|
||||||
tasks = this.tasksService.failedFileTasks
|
tasks = this.tasksService.failedFileTasks
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
if (this._filterText.length) {
|
||||||
|
tasks = tasks.filter((t) => {
|
||||||
|
if (this.filterTargetID == TaskFilterTargetID.Name) {
|
||||||
|
return t.task_file_name
|
||||||
|
.toLowerCase()
|
||||||
|
.includes(this._filterText.toLowerCase())
|
||||||
|
} else if (this.filterTargetID == TaskFilterTargetID.Result) {
|
||||||
|
return t.result.toLowerCase().includes(this._filterText.toLowerCase())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
return tasks
|
return tasks
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,19 +192,24 @@ export class TasksComponent
|
|||||||
this.selectedTasks.clear()
|
this.selectedTasks.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
duringTabChange(navID: number) {
|
duringTabChange() {
|
||||||
this.page = 1
|
this.page = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
beforeTabChange() {
|
||||||
|
this.resetFilter()
|
||||||
|
this.filterTargetID = TaskFilterTargetID.Name
|
||||||
|
}
|
||||||
|
|
||||||
get activeTabLocalized(): string {
|
get activeTabLocalized(): string {
|
||||||
switch (this.activeTab) {
|
switch (this.activeTab) {
|
||||||
case 'queued':
|
case TaskTab.Queued:
|
||||||
return $localize`queued`
|
return $localize`queued`
|
||||||
case 'started':
|
case TaskTab.Started:
|
||||||
return $localize`started`
|
return $localize`started`
|
||||||
case 'completed':
|
case TaskTab.Completed:
|
||||||
return $localize`completed`
|
return $localize`completed`
|
||||||
case 'failed':
|
case TaskTab.Failed:
|
||||||
return $localize`failed`
|
return $localize`failed`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -152,4 +224,16 @@ export class TasksComponent
|
|||||||
}, 5000)
|
}, 5000)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public resetFilter() {
|
||||||
|
this._filterText = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
filterInputKeyup(event: KeyboardEvent) {
|
||||||
|
if (event.key == 'Enter') {
|
||||||
|
this._filterText = (event.target as HTMLInputElement).value
|
||||||
|
} else if (event.key === 'Escape') {
|
||||||
|
this.resetFilter()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user