mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-04-02 13:45:10 -05:00
rework of the front end components
This commit is contained in:
parent
339e96b63c
commit
ede3bd1391
@ -32,7 +32,7 @@ export class AppComponent implements OnInit, OnDestroy {
|
||||
|
||||
this.successSubscription = this.consumerStatusService.onDocumentConsumptionFinished().subscribe(status => {
|
||||
this.toastService.show({title: "Document added", delay: 10000, content: `Document ${status.filename} was added to paperless.`, actionName: "Open document", action: () => {
|
||||
this.router.navigate(['documents', status.document_id])
|
||||
this.router.navigate(['documents', status.documentId])
|
||||
}})
|
||||
})
|
||||
|
||||
@ -42,6 +42,4 @@ export class AppComponent implements OnInit, OnDestroy {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@ -9,10 +9,16 @@
|
||||
|
||||
</ngx-file-drop>
|
||||
</form>
|
||||
<div *ngIf="uploadVisible" class="mt-3">
|
||||
<p i18n>{uploadStatus.length, plural, =1 {Uploading file...} =other {Uploading {{uploadStatus.length}} files...}}</p>
|
||||
<ngb-progressbar [value]="loadedSum" [max]="totalSum" [striped]="true" [animated]="uploadStatus.length > 0">
|
||||
<div *ngFor="let status of getStatus()">
|
||||
<p>{{status.filename}}: {{status.message}}</p>
|
||||
<ngb-progressbar [value]="status.getProgress()" [max]="1" [striped]="true" [animated]="!isFinished(status)" [type]="getType(status)">
|
||||
</ngb-progressbar>
|
||||
|
||||
<div *ngIf="isFinished(status)" class="mb-2">
|
||||
<button *ngIf="status.documentId" class="btn btn-sm btn-outline-primary mr-2" routerLink="/documents/{{status.documentId}}" (click)="dismiss(status)">Open document</button>
|
||||
<button class="btn btn-sm btn-outline-secondary" (click)="dismiss(status)">Dismiss</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</app-widget-frame>
|
@ -1,15 +1,10 @@
|
||||
import { HttpEventType } from '@angular/common/http';
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { FileSystemFileEntry, NgxFileDropEntry } from 'ngx-file-drop';
|
||||
import { ConsumerStatusService, FileStatus, FileStatusPhase } from 'src/app/services/consumer-status.service';
|
||||
import { DocumentService } from 'src/app/services/rest/document.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
|
||||
interface UploadStatus {
|
||||
loaded: number
|
||||
total: number
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-upload-file-widget',
|
||||
templateUrl: './upload-file-widget.component.html',
|
||||
@ -17,7 +12,34 @@ interface UploadStatus {
|
||||
})
|
||||
export class UploadFileWidgetComponent implements OnInit {
|
||||
|
||||
constructor(private documentService: DocumentService, private toastService: ToastService) { }
|
||||
constructor(
|
||||
private documentService: DocumentService,
|
||||
private consumerStatusService: ConsumerStatusService
|
||||
) { }
|
||||
|
||||
getStatus() {
|
||||
return this.consumerStatusService.consumerStatus
|
||||
}
|
||||
|
||||
isFinished(status: FileStatus) {
|
||||
return status.phase == FileStatusPhase.FAILED || status.phase == FileStatusPhase.SUCCESS
|
||||
}
|
||||
|
||||
getType(status: FileStatus) {
|
||||
switch (status.phase) {
|
||||
case FileStatusPhase.PROCESSING:
|
||||
case FileStatusPhase.UPLOADING:
|
||||
return "primary"
|
||||
case FileStatusPhase.FAILED:
|
||||
return "danger"
|
||||
case FileStatusPhase.SUCCESS:
|
||||
return "success"
|
||||
}
|
||||
}
|
||||
|
||||
dismiss(status: FileStatus) {
|
||||
this.consumerStatusService.dismiss(status)
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
@ -28,54 +50,37 @@ export class UploadFileWidgetComponent implements OnInit {
|
||||
public fileLeave(event){
|
||||
}
|
||||
|
||||
uploadStatus: UploadStatus[] = []
|
||||
completedFiles = 0
|
||||
|
||||
uploadVisible = false
|
||||
|
||||
get loadedSum() {
|
||||
return this.uploadStatus.map(s => s.loaded).reduce((a,b) => a+b, this.completedFiles > 0 ? 1 : 0)
|
||||
}
|
||||
|
||||
get totalSum() {
|
||||
return this.uploadStatus.map(s => s.total).reduce((a,b) => a+b, 1)
|
||||
}
|
||||
|
||||
public dropped(files: NgxFileDropEntry[]) {
|
||||
for (const droppedFile of files) {
|
||||
if (droppedFile.fileEntry.isFile) {
|
||||
let uploadStatusObject: UploadStatus = {loaded: 0, total: 1}
|
||||
this.uploadStatus.push(uploadStatusObject)
|
||||
this.uploadVisible = true
|
||||
|
||||
const fileEntry = droppedFile.fileEntry as FileSystemFileEntry;
|
||||
fileEntry.file((file: File) => {
|
||||
let formData = new FormData()
|
||||
formData.append('document', file, file.name)
|
||||
let status = this.consumerStatusService.newFileUpload()
|
||||
status.filename = file.name
|
||||
|
||||
this.documentService.uploadDocument(formData).subscribe(event => {
|
||||
if (event.type == HttpEventType.UploadProgress) {
|
||||
uploadStatusObject.loaded = event.loaded
|
||||
uploadStatusObject.total = event.total
|
||||
status.updateProgress(FileStatusPhase.UPLOADING, event.loaded, event.total)
|
||||
} else if (event.type == HttpEventType.Response) {
|
||||
this.uploadStatus.splice(this.uploadStatus.indexOf(uploadStatusObject), 1)
|
||||
this.completedFiles += 1
|
||||
this.toastService.showInfo($localize`The document has been uploaded and will be processed by the consumer shortly.`)
|
||||
status.taskId = event.body["task_id"]
|
||||
}
|
||||
|
||||
}, error => {
|
||||
this.uploadStatus.splice(this.uploadStatus.indexOf(uploadStatusObject), 1)
|
||||
this.completedFiles += 1
|
||||
status.updateProgress(FileStatusPhase.FAILED)
|
||||
switch (error.status) {
|
||||
case 400: {
|
||||
this.toastService.showInfo($localize`There was an error while uploading the document: ${error.error.document}`)
|
||||
status.message = error.error.document
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
this.toastService.showInfo($localize`An error has occurred while uploading the document. Sorry!`)
|
||||
status.message = $localize`An error has occurred while uploading the document. Sorry!`
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
});
|
||||
}
|
||||
|
11
src-ui/src/app/data/websocket-consumer-status-message.ts
Normal file
11
src-ui/src/app/data/websocket-consumer-status-message.ts
Normal file
@ -0,0 +1,11 @@
|
||||
export interface WebsocketConsumerStatusMessage {
|
||||
|
||||
filename?: string
|
||||
task_id?: string
|
||||
current_progress?: number
|
||||
max_progress?: number
|
||||
status?: string
|
||||
message?: string
|
||||
document_id: number
|
||||
|
||||
}
|
@ -1,13 +1,57 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Subject } from 'rxjs';
|
||||
import { WebsocketConsumerStatusMessage } from '../data/websocket-consumer-status-message';
|
||||
|
||||
export enum FileStatusPhase {
|
||||
STARTED = 0,
|
||||
UPLOADING = 1,
|
||||
PROCESSING = 2,
|
||||
SUCCESS = 3,
|
||||
FAILED = 4
|
||||
}
|
||||
|
||||
export class FileStatus {
|
||||
|
||||
filename: string
|
||||
|
||||
taskId: string
|
||||
|
||||
phase: FileStatusPhase = FileStatusPhase.STARTED
|
||||
|
||||
currentPhaseProgress: number
|
||||
|
||||
currentPhaseMaxProgress: number
|
||||
|
||||
message: string
|
||||
|
||||
documentId: number
|
||||
|
||||
getProgress(): number {
|
||||
switch (this.phase) {
|
||||
case FileStatusPhase.STARTED:
|
||||
return 0.0
|
||||
case FileStatusPhase.UPLOADING:
|
||||
return this.currentPhaseProgress / this.currentPhaseMaxProgress * 0.2
|
||||
case FileStatusPhase.PROCESSING:
|
||||
return this.currentPhaseProgress / this.currentPhaseMaxProgress * 0.8 + 0.2
|
||||
case FileStatusPhase.SUCCESS:
|
||||
case FileStatusPhase.FAILED:
|
||||
return 1.0
|
||||
}
|
||||
}
|
||||
|
||||
updateProgress(status: FileStatusPhase, currentProgress?: number, maxProgress?: number) {
|
||||
if (status >= this.phase) {
|
||||
this.phase = status
|
||||
if (currentProgress) {
|
||||
this.currentPhaseProgress = currentProgress
|
||||
}
|
||||
if (maxProgress) {
|
||||
this.currentPhaseMaxProgress = maxProgress
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface FileStatus {
|
||||
filename?: string
|
||||
current_progress?: number
|
||||
max_progress?: number
|
||||
status?: string
|
||||
message?: string
|
||||
document_id?: number
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
@ -23,24 +67,41 @@ export class ConsumerStatusService {
|
||||
private documentConsumptionFinishedSubject = new Subject<FileStatus>()
|
||||
private documentConsumptionFailedSubject = new Subject<FileStatus>()
|
||||
|
||||
private get(taskId: string, filename?: string) {
|
||||
let status = this.consumerStatus.find(e => e.taskId == taskId) || this.consumerStatus.find(e => e.filename == filename)
|
||||
if (!status) {
|
||||
status = new FileStatus()
|
||||
this.consumerStatus.push(status)
|
||||
}
|
||||
status.taskId = taskId
|
||||
status.filename = filename
|
||||
return status
|
||||
}
|
||||
|
||||
newFileUpload(): FileStatus {
|
||||
let status = new FileStatus()
|
||||
this.consumerStatus.push(status)
|
||||
return status
|
||||
}
|
||||
|
||||
connect() {
|
||||
this.disconnect()
|
||||
this.statusWebSocked = new WebSocket("ws://localhost:8000/ws/status/");
|
||||
this.statusWebSocked.onmessage = (ev) => {
|
||||
let statusUpdate: FileStatus = JSON.parse(ev['data'])
|
||||
let statusMessage: WebsocketConsumerStatusMessage = JSON.parse(ev['data'])
|
||||
|
||||
let index = this.consumerStatus.findIndex(fs => fs.filename == statusUpdate.filename)
|
||||
if (index > -1) {
|
||||
this.consumerStatus[index] = statusUpdate
|
||||
} else {
|
||||
this.consumerStatus.push(statusUpdate)
|
||||
}
|
||||
let status = this.get(statusMessage.task_id, statusMessage.filename)
|
||||
status.updateProgress(FileStatusPhase.PROCESSING, statusMessage.current_progress, statusMessage.max_progress)
|
||||
status.message = statusMessage.message
|
||||
status.documentId = statusMessage.document_id
|
||||
|
||||
if (statusUpdate.status == "SUCCESS") {
|
||||
this.documentConsumptionFinishedSubject.next(statusUpdate)
|
||||
if (statusMessage.status == "SUCCESS") {
|
||||
status.phase = FileStatusPhase.SUCCESS
|
||||
this.documentConsumptionFinishedSubject.next(status)
|
||||
}
|
||||
if (statusUpdate.status == "FAILED") {
|
||||
this.documentConsumptionFailedSubject.next(statusUpdate)
|
||||
if (statusMessage.status == "FAILED") {
|
||||
status.phase = FileStatusPhase.FAILED
|
||||
this.documentConsumptionFailedSubject.next(status)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user