Reprot more accurate notification for tasks with follow-ups

This commit is contained in:
shamoon committed 2026-11-14 11:51:42 -08:00
1 parent 6f5a192967
commit 058e27e3ef
8 files changed
+84 -14

No files matched your search

@@ -899,6 +899,7 @@ describe('TasksComponent', () => {
['modify_tags', 12, 'Bulk operation: Update tags', '(12 documents)'],
['run_workflow', 1, 'Run workflow', null],
['delete', 3, 'Bulk operation: Move to trash', '(3 documents)'],
['reprocess', 2, 'Bulk operation: Document reprocessing', '(2 documents)'],
])(
'shows the operation, document count and outcome for %s',
(operation, count, label, summary) => {
@@ -933,7 +934,19 @@ describe('TasksComponent', () => {
expect(name.textContent).not.toContain(task.task_type_display)
expect(
name.closest('tr').querySelector('.results-col').textContent
).toContain('Completed')
).toContain(
[
'reprocess',
'rotate',
'merge',
'split',
'delete_pages',
'edit_pdf',
'remove_password',
].includes(operation)
? 'Submitted for processing'
: 'Completed'
)
}
)
@@ -33,7 +33,10 @@ import { IfPermissionsDirective } from 'src/app/directives/if-permissions.direct
import { CustomDatePipe } from 'src/app/pipes/custom-date.pipe'
import { TasksService } from 'src/app/services/tasks.service'
import { ToastService } from 'src/app/services/toast.service'
import { getDocumentOperationLabel } from 'src/app/utils/document-operation'
import {
documentOperationHasFollowUpTasks,
getDocumentOperationLabel,
} from 'src/app/utils/document-operation'
import { ConfirmDialogComponent } from '../../common/confirm-dialog/confirm-dialog.component'
import { PageHeaderComponent } from '../../common/page-header/page-header.component'
import { LoadingComponentWithPermissions } from '../../loading-component/loading.component'
@@ -470,7 +473,9 @@ export class TasksComponent
task.status === PaperlessTaskStatus.Success &&
this.taskOperationLabel(task)
) {
return $localize`Completed`
return documentOperationHasFollowUpTasks(task.input_data?.operation)
? $localize`Submitted for processing`
: $localize`Completed`
}
return null
@@ -1564,7 +1564,7 @@ export class DocumentDetailComponent
this.taskNotifications.showQueued(
task_id,
$localize`Document reprocessing queued.`,
{ content: $localize`Document reprocessing completed.` }
{ content: $localize`Document submitted for reprocessing.` }
)
if (modal) {
modal.close()
@@ -2002,7 +2002,7 @@ export class DocumentDetailComponent
task_id,
$localize`PDF edit queued.`,
{
content: $localize`PDF edit completed.`,
content: $localize`PDF edit submitted for processing.`,
}
)
modal.close()
@@ -2059,7 +2059,9 @@ export class DocumentDetailComponent
this.taskNotifications.showQueued(
task_id,
$localize`Password removal queued.`,
{ content: $localize`Password removal completed.` }
{
content: $localize`Password removal submitted for processing.`,
}
)
this.networkActive.set(false)
modal.close()
@@ -1388,6 +1388,7 @@ describe('BulkEditorComponent', () => {
})
it('should support rotate', () => {
const toast = jest.spyOn(toastService, 'show')
let modal: NgbModalRef
modalService.activeInstances.subscribe((m) => (modal = m[0]))
jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(true)
@@ -1409,6 +1410,14 @@ describe('BulkEditorComponent', () => {
`${environment.apiBaseUrl}documents/rotate/`
)
req.flush({ result: 'OK', task_id: 'rotate-1' })
TestBed.inject(TaskNotificationService).complete({
task_id: 'rotate-1',
operation: 'rotate',
status: PaperlessTaskStatus.Success,
})
expect(toast.mock.calls.at(-1)[0].content).toBe(
"Bulk operation 'Rotate documents' submitted for processing."
)
expect(req.request.body).toEqual({
documents: [3, 4],
degrees: 90,
@@ -53,7 +53,10 @@ import { SettingsService } from 'src/app/services/settings.service'
import { TaskNotificationService } from 'src/app/services/task-notification.service'
import { ToastService } from 'src/app/services/toast.service'
import { TrashService } from 'src/app/services/trash.service'
import { getDocumentOperationLabel } from 'src/app/utils/document-operation'
import {
documentOperationHasFollowUpTasks,
getDocumentOperationLabel,
} from 'src/app/utils/document-operation'
import { flattenTags } from 'src/app/utils/flatten-tags'
import { queryParamsFromFilterRules } from 'src/app/utils/query-params'
import { MergeAsVersionsConfirmDialogComponent } from '../../common/confirm-dialog/merge-as-versions-confirm-dialog/merge-as-versions-confirm-dialog.component'
@@ -374,9 +377,13 @@ export class BulkEditorComponent
? $localize`Bulk operation '${label}' queued.`
: $localize`Operation '${label}' queued.`,
{
content: bulk
? $localize`Bulk operation '${label}' completed.`
: $localize`Operation '${label}' completed.`,
content: documentOperationHasFollowUpTasks(operation)
? bulk
? $localize`Bulk operation '${label}' submitted for processing.`
: $localize`Operation '${label}' submitted for processing.`
: bulk
? $localize`Bulk operation '${label}' completed.`
: $localize`Operation '${label}' completed.`,
}
)
}
@@ -81,6 +81,22 @@ describe('TaskNotificationService', () => {
])
})
it.each([
'reprocess',
'rotate',
'merge',
'split',
'delete_pages',
'edit_pdf',
'remove_password',
])('reports %s as submitted rather than completed', (operation) => {
service.showQueued('task-1', 'Queued')
service.complete({ ...result(), operation })
expect(notifications[0].content).toContain('submitted for processing.')
expect(notifications[0].content).not.toContain('completed')
expect(notifications[0].action).toBeUndefined()
})
it('does not show a stale queued state when completion beats the HTTP response', () => {
service.complete(result())
service.showQueued('task-1', 'Workflow queued.', {
@@ -2,7 +2,10 @@ import { inject, Injectable, OnDestroy } from '@angular/core'
import { Router } from '@angular/router'
import { BulkOperationStatus } from '../data/bulk-operation'
import { PaperlessTaskStatus } from '../data/paperless-task'
import { getDocumentOperationLabel } from '../utils/document-operation'
import {
documentOperationHasFollowUpTasks,
getDocumentOperationLabel,
} from '../utils/document-operation'
import {
PermissionAction,
PermissionsService,
@@ -80,9 +83,11 @@ export class TaskNotificationService implements OnDestroy {
const operation = getDocumentOperationLabel(result.operation)
let content: string
if (succeeded) {
content = operation
? $localize`Operation '${operation}' completed.`
: $localize`Operation completed.`
content = documentOperationHasFollowUpTasks(result.operation)
? $localize`Operation '${operation}' submitted for processing.`
: operation
? $localize`Operation '${operation}' completed.`
: $localize`Operation completed.`
} else if (result.status === PaperlessTaskStatus.Revoked) {
content = operation
? $localize`Operation '${operation}' revoked.`
@@ -23,3 +23,16 @@ export function getDocumentOperationLabel(operation?: string): string | null {
const label = operation ? OPERATION_LABELS[operation] : null
return label ?? null
}
// These operations queue processing in separate tasks
export function documentOperationHasFollowUpTasks(operation?: string): boolean {
return [
'reprocess',
'rotate',
'merge',
'split',
'delete_pages',
'edit_pdf',
'remove_password',
].includes(operation ?? '')
}