modular dashboard

This commit is contained in:
Jonas Winkler 2020-11-22 22:35:39 +01:00
parent 4b47f4929e
commit fa5df5d28e
19 changed files with 288 additions and 118 deletions

View File

@ -41,6 +41,10 @@ import { TagsComponent } from './components/common/input/tags/tags.component';
import { SortableDirective } from './directives/sortable.directive';
import { CookieService } from 'ngx-cookie-service';
import { CsrfInterceptor } from './interceptors/csrf.interceptor';
import { SavedViewWidgetComponent } from './components/dashboard/widgets/saved-view-widget/saved-view-widget.component';
import { StatisticsWidgetComponent } from './components/dashboard/widgets/statistics-widget/statistics-widget.component';
import { UploadFileWidgetComponent } from './components/dashboard/widgets/upload-file-widget/upload-file-widget.component';
import { WidgetFrameComponent } from './components/dashboard/widgets/widget-frame/widget-frame.component';
@NgModule({
declarations: [
@ -74,7 +78,11 @@ import { CsrfInterceptor } from './interceptors/csrf.interceptor';
SaveViewConfigDialogComponent,
DateTimeComponent,
TagsComponent,
SortableDirective
SortableDirective,
SavedViewWidgetComponent,
StatisticsWidgetComponent,
UploadFileWidgetComponent,
WidgetFrameComponent
],
imports: [
BrowserModule,

View File

@ -4,77 +4,22 @@
<div class='row'>
<div class="col-lg">
<ng-container *ngFor="let v of savedDashboardViews">
<div class="card mb-3 shadow">
<div class="card-header">
<h5 class="card-title mb-0">{{v.viewConfig.title}}</h5>
</div>
<div class="card-body text-dark">
<table class="table table-sm table-hover table-borderless">
<thead>
<tr>
<th>Created</th>
<th scope="col">Title</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let doc of v.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>
</div>
</div>
</ng-container>
<ng-container *ngIf="savedDashboardViews.length == 0">
<div class="card mb-3 shadow">
<div class="card-header">
<h5 class="card-title mb-0">Saved views</h5>
</div>
<div class="card-body text-dark">
<p class="card-text">This space is reserved to display your saved views. Go to your documents and save a view
to have it displayed
here!</p>
</div>
</div>
<app-widget-frame title="Saved views" *ngIf="savedViews.length == 0">
<p class="card-text">This space is reserved to display your saved views. Go to your documents and save a view
to have it displayed
here!</p>
</app-widget-frame>
<ng-container *ngFor="let v of savedViews">
<app-saved-view-widget [savedView]="v"></app-saved-view-widget>
</ng-container>
</div>
<div class="col-lg">
<div class="card mb-3 shadow">
<div class="card-header">
<h5 class="card-title mb-0">Statistics</h5>
</div>
<div class="card-body text-dark">
<p class="card-text">Documents in inbox: {{statistics.documents_inbox}}</p>
<p class="card-text">Total documents: {{statistics.documents_total}}</p>
</div>
</div>
<app-statistics-widget></app-statistics-widget>
<div class="card mb-3 shadow">
<div class="card-header">
<h5 class="card-title mb-0">Upload new documents</h5>
</div>
<div class="card-body text-dark">
<form>
<ngx-file-drop
dropZoneLabel="Drop documents here or" (onFileDrop)="dropped($event)"
(onFileOver)="fileOver($event)" (onFileLeave)="fileLeave($event)"
dropZoneClassName="bg-light card"
multiple="true"
contentClassName="justify-content-center d-flex align-items-center p-5"
[showBrowseBtn]=true
browseBtnClassName="btn btn-sm btn-outline-primary ml-2">
</ngx-file-drop>
</form>
</div>
</div>
<app-upload-file-widget></app-upload-file-widget>
</div>
</div>

View File

@ -1,16 +1,6 @@
import { HttpClient } from '@angular/common/http';
import { Component, OnInit } from '@angular/core';
import { FileSystemFileEntry, NgxFileDropEntry } from 'ngx-file-drop';
import { Observable } from 'rxjs';
import { DocumentService } from 'src/app/services/rest/document.service';
import { SavedViewConfigService } from 'src/app/services/saved-view-config.service';
import { Toast, ToastService } from 'src/app/services/toast.service';
import { environment } from 'src/environments/environment';
export interface Statistics {
documents_total?: number
documents_inbox?: number
}
@Component({
selector: 'app-dashboard',
@ -19,53 +9,14 @@ export interface Statistics {
})
export class DashboardComponent implements OnInit {
constructor(private documentService: DocumentService, private toastService: ToastService,
public savedViewConfigService: SavedViewConfigService, private http: HttpClient) { }
constructor(
public savedViewConfigService: SavedViewConfigService) { }
savedDashboardViews = []
statistics: Statistics = {}
savedViews = []
ngOnInit(): void {
this.savedViewConfigService.getDashboardConfigs().forEach(config => {
this.documentService.list(1,10,config.sortField,config.sortDirection,config.filterRules).subscribe(result => {
this.savedDashboardViews.push({viewConfig: config, documents: result.results})
})
})
this.getStatistics().subscribe(statistics => {
this.statistics = statistics
})
this.savedViews = this.savedViewConfigService.getDashboardConfigs()
}
getStatistics(): Observable<Statistics> {
return this.http.get(`${environment.apiBaseUrl}statistics/`)
}
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.showToast(Toast.make("Information", "The document has been uploaded and will be processed by the consumer shortly."))
}, error => {
this.toastService.showToast(Toast.makeError("An error has occured while uploading the document. Sorry!"))
})
});
}
}
}
}

View File

@ -0,0 +1,18 @@
<app-widget-frame [title]="savedView.title">
<table class="table table-sm table-hover table-borderless">
<thead>
<tr>
<th>Created</th>
<th scope="col">Title</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>
</app-widget-frame>

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,26 @@
import { Component, Input, OnInit } from '@angular/core';
import { PaperlessDocument } from 'src/app/data/paperless-document';
import { SavedViewConfig } from 'src/app/data/saved-view-config';
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.scss']
})
export class SavedViewWidgetComponent implements OnInit {
constructor(private documentService: DocumentService) { }
@Input()
savedView: SavedViewConfig
documents: PaperlessDocument[] = []
ngOnInit(): void {
this.documentService.list(1,10,this.savedView.sortField,this.savedView.sortDirection,this.savedView.filterRules).subscribe(result => {
this.documents = result.results
})
}
}

View File

@ -0,0 +1,4 @@
<app-widget-frame title="Statistics">
<p class="card-text">Documents in inbox: {{statistics.documents_inbox}}</p>
<p class="card-text">Total documents: {{statistics.documents_total}}</p>
</app-widget-frame>

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,33 @@
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.scss']
})
export class StatisticsWidgetComponent implements OnInit {
constructor(private http: HttpClient) { }
statistics: Statistics = {}
getStatistics(): Observable<Statistics> {
return this.http.get(`${environment.apiBaseUrl}statistics/`)
}
ngOnInit(): void {
this.getStatistics().subscribe(statistics => {
this.statistics = statistics
})
}
}

View File

@ -0,0 +1,15 @@
<app-widget-frame title="Upload new documents">
<form>
<ngx-file-drop
dropZoneLabel="Drop documents here or" (onFileDrop)="dropped($event)"
(onFileOver)="fileOver($event)" (onFileLeave)="fileLeave($event)"
dropZoneClassName="bg-light card"
multiple="true"
contentClassName="justify-content-center d-flex align-items-center p-5"
[showBrowseBtn]=true
browseBtnClassName="btn btn-sm btn-outline-primary ml-2">
</ngx-file-drop>
</form>
</app-widget-frame>

View File

@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { UploadFileWidgetComponent } from './upload-file-widget.component';
describe('UploadFileWidgetComponent', () => {
let component: UploadFileWidgetComponent;
let fixture: ComponentFixture<UploadFileWidgetComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ UploadFileWidgetComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(UploadFileWidgetComponent);
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 { Toast, ToastService } from 'src/app/services/toast.service';
@Component({
selector: 'app-upload-file-widget',
templateUrl: './upload-file-widget.component.html',
styleUrls: ['./upload-file-widget.component.scss']
})
export class UploadFileWidgetComponent 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.showToast(Toast.make("Information", "The document has been uploaded and will be processed by the consumer shortly."))
}, error => {
this.toastService.showToast(Toast.makeError("An error has occured while uploading the document. Sorry!"))
})
});
}
}
}
}

View File

@ -0,0 +1,8 @@
<div class="card mb-3 shadow">
<div class="card-header">
<h5 class="card-title mb-0">{{title}}</h5>
</div>
<div class="card-body text-dark">
<ng-content></ng-content>
</div>
</div>

View File

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

View File

@ -0,0 +1,18 @@
import { Component, Input, OnInit } from '@angular/core';
@Component({
selector: 'app-widget-frame',
templateUrl: './widget-frame.component.html',
styleUrls: ['./widget-frame.component.scss']
})
export class WidgetFrameComponent implements OnInit {
constructor() { }
@Input()
title: string
ngOnInit(): void {
}
}