better toasts, better dashboard, first implementation of consumer status

This commit is contained in:
Jonas Winkler
2020-11-07 12:05:15 +01:00
parent 572e40ca27
commit 036f11acaa
28 changed files with 450 additions and 134 deletions

View File

@@ -0,0 +1,10 @@
<h4 class="mt-3">Document consumer status</h4>
<div class="p-2 mb-1 border bg-light" *ngFor="let s of getStatus()">
<div class="mb-1"><strong>{{s.filename}}:</strong> {{s.message}}</div>
<ngb-progressbar [type]="getType(s.status)" [value]="s.current_progress" [max]="s.max_progress" class="mb-2"></ngb-progressbar>
<div *ngIf="isFinished(s)">
<button *ngIf="s.document_id" class="btn btn-sm btn-outline-secondary mr-2" routerLink="/documents/{{s.document_id}}" (click)="dismiss(s)">Open document</button>
<button class="btn btn-sm btn-outline-secondary" (click)="dismiss(s)">Dismiss</button>
</div>
</div>

View File

@@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ConsumerStatusWidgetComponent } from './consumer-status-widget.component';
describe('ConsumerStatusWidgetComponent', () => {
let component: ConsumerStatusWidgetComponent;
let fixture: ComponentFixture<ConsumerStatusWidgetComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ ConsumerStatusWidgetComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(ConsumerStatusWidgetComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,35 @@
import { Component, OnInit } from '@angular/core';
import { ConsumerStatusService, FileStatus } from 'src/app/services/consumer-status.service';
@Component({
selector: 'app-consumer-status-widget',
templateUrl: './consumer-status-widget.component.html',
styleUrls: ['./consumer-status-widget.component.css']
})
export class ConsumerStatusWidgetComponent implements OnInit {
constructor(private consumerStatusService: ConsumerStatusService) { }
ngOnInit(): void {
}
getStatus() {
return this.consumerStatusService.consumerStatus
}
isFinished(status: FileStatus) {
return status.status == "FAILED" || status.status == "SUCCESS"
}
getType(status) {
switch (status) {
case "WORKING": return "primary"
case "FAILED": return "danger"
case "SUCCESS": return "success"
}
}
dismiss(status: FileStatus) {
this.consumerStatusService.dismiss(status)
}
}

View File

@@ -0,0 +1,11 @@
<h4>Upload new Document</h4>
<form>
<ngx-file-drop
dropZoneLabel="Drop documents here"
(onFileDrop)="dropped($event)"
(onFileOver)="fileOver($event)"
(onFileLeave)="fileLeave($event)"
dropZoneClassName="bg-light mt-4 card">
</ngx-file-drop>
</form>

View File

@@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { FileUploadWidgetComponent } from './file-upload-widget.component';
describe('FileUploadWidgetComponent', () => {
let component: FileUploadWidgetComponent;
let fixture: ComponentFixture<FileUploadWidgetComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ FileUploadWidgetComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(FileUploadWidgetComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,44 @@
import { Component, OnInit } from '@angular/core';
import { FileSystemFileEntry, NgxFileDropEntry } from 'ngx-file-drop';
import { DocumentService } from 'src/app/services/rest/document.service';
import { ToastService } from 'src/app/services/toast.service';
@Component({
selector: 'app-file-upload-widget',
templateUrl: './file-upload-widget.component.html',
styleUrls: ['./file-upload-widget.component.css']
})
export class FileUploadWidgetComponent implements OnInit {
constructor(private documentService: DocumentService, private toastService: ToastService) { }
ngOnInit(): void {
}
public fileOver(event){
console.log(event);
}
public fileLeave(event){
console.log(event);
}
public dropped(files: NgxFileDropEntry[]) {
for (const droppedFile of files) {
if (droppedFile.fileEntry.isFile) {
const fileEntry = droppedFile.fileEntry as FileSystemFileEntry;
console.log(fileEntry)
fileEntry.file((file: File) => {
console.log(file)
const formData = new FormData()
formData.append('document', file, file.name)
this.documentService.uploadDocument(formData).subscribe(result => {
this.toastService.showInfo("The document has been uploaded and will be processed by the consumer shortly.")
}, error => {
this.toastService.showError("An error has occured while uploading the document. Sorry!")
})
});
}
}
}
}

View File

@@ -0,0 +1,16 @@
<h4>{{viewConfig.title}}</h4>
<table class="table table-sm table-hover table-borderless">
<thead>
<tr>
<th>Date created</th>
<th scope="col">Document</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let doc of documents" routerLink="/documents/{{doc.id}}">
<td>{{doc.created | date}}</td>
<td>{{doc.title}}<app-tag [tag]="t" *ngFor="let t of doc.tags" class="ml-1"></app-tag>
</tr>
</tbody>
</table>

View File

@@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { SavedViewWidgetComponent } from './saved-view-widget.component';
describe('SavedViewWidgetComponent', () => {
let component: SavedViewWidgetComponent;
let fixture: ComponentFixture<SavedViewWidgetComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ SavedViewWidgetComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(SavedViewWidgetComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,41 @@
import { Component, Input, OnDestroy, OnInit } from '@angular/core';
import { Subscription } from 'rxjs';
import { PaperlessDocument } from 'src/app/data/paperless-document';
import { SavedViewConfig } from 'src/app/data/saved-view-config';
import { ConsumerStatusService } from 'src/app/services/consumer-status.service';
import { DocumentService } from 'src/app/services/rest/document.service';
@Component({
selector: 'app-saved-view-widget',
templateUrl: './saved-view-widget.component.html',
styleUrls: ['./saved-view-widget.component.css']
})
export class SavedViewWidgetComponent implements OnInit, OnDestroy {
constructor(private documentService: DocumentService, private consumerStatusService: ConsumerStatusService) { }
@Input()
viewConfig: SavedViewConfig
documents: PaperlessDocument[]
subscription: Subscription
ngOnInit(): void {
this.reload()
this.subscription = this.consumerStatusService.onDocumentConsumptionFinished().subscribe(status => {
this.reload()
})
}
ngOnDestroy(): void {
this.subscription.unsubscribe()
}
reload() {
this.documentService.list(1,10,this.viewConfig.sortField,this.viewConfig.sortDirection,this.viewConfig.filterRules).subscribe(result => {
this.documents = result.results
})
}
}

View File

@@ -0,0 +1,3 @@
<h4>Statistics</h4>
<p>Documents in inbox: {{statistics.documents_inbox}}</p>
<p>Total documents: {{statistics.documents_total}}</p>

View File

@@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { StatisticsWidgetComponent } from './statistics-widget.component';
describe('StatisticsWidgetComponent', () => {
let component: StatisticsWidgetComponent;
let fixture: ComponentFixture<StatisticsWidgetComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ StatisticsWidgetComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(StatisticsWidgetComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,32 @@
import { HttpClient } from '@angular/common/http';
import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
import { environment } from 'src/environments/environment';
export interface Statistics {
documents_total?: number
documents_inbox?: number
}
@Component({
selector: 'app-statistics-widget',
templateUrl: './statistics-widget.component.html',
styleUrls: ['./statistics-widget.component.css']
})
export class StatisticsWidgetComponent implements OnInit {
constructor(private http: HttpClient) { }
statistics: Statistics = {}
ngOnInit(): void {
this.getStatistics().subscribe(statistics => {
this.statistics = statistics
})
}
getStatistics(): Observable<Statistics> {
return this.http.get(`${environment.apiBaseUrl}statistics/`)
}
}