Merge branch 'dev' into celery-tasks

This commit is contained in:
Jonas Winkler
2020-11-10 00:16:59 +01:00
77 changed files with 1605 additions and 959 deletions

View File

@@ -10,7 +10,7 @@
</form>
<ul class="navbar-nav px-3">
<li class="nav-item text-nowrap">
<a class="nav-link" (click)="logout()" style="cursor: pointer;">
<a class="nav-link" href="accounts/logout/">
<svg class="buttonicon" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#door-closed"/>
</svg>

View File

@@ -1,10 +1,9 @@
import { Component, OnDestroy, OnInit } from '@angular/core';
import { FormControl } from '@angular/forms';
import { Router } from '@angular/router';
import { from, Observable, of, scheduled, Subscription } from 'rxjs';
import { from, Observable, Subscription } from 'rxjs';
import { debounceTime, distinctUntilChanged, map, switchMap } from 'rxjs/operators';
import { PaperlessDocument } from 'src/app/data/paperless-document';
import { AuthService } from 'src/app/services/auth.service';
import { OpenDocumentsService } from 'src/app/services/open-documents.service';
import { SearchService } from 'src/app/services/rest/search.service';
import { SavedViewConfigService } from 'src/app/services/saved-view-config.service';
@@ -19,7 +18,6 @@ export class AppFrameComponent implements OnInit, OnDestroy {
constructor (
public router: Router,
private openDocumentsService: OpenDocumentsService,
private authService: AuthService,
private searchService: SearchService,
public viewConfigService: SavedViewConfigService
) {
@@ -64,10 +62,6 @@ export class AppFrameComponent implements OnInit, OnDestroy {
this.router.navigate(['search'], {queryParams: {query: this.searchField.value}})
}
logout() {
this.authService.logout()
}
ngOnInit() {
this.openDocuments = this.openDocumentsService.getOpenDocuments()
}

View File

@@ -134,8 +134,8 @@ export class DocumentDetailComponent implements OnInit {
close() {
this.openDocumentService.closeDocument(this.document)
if (this.documentListViewService.viewConfig) {
this.router.navigate(['view', this.documentListViewService.viewConfig.id])
if (this.documentListViewService.viewId) {
this.router.navigate(['view', this.documentListViewService.viewId])
} else {
this.router.navigate(['documents'])
}

View File

@@ -1,4 +1,4 @@
<app-page-header [title]="docs.viewConfig ? docs.viewConfig.title : 'Documents'">
<app-page-header [title]="getTitle()">
<div class="btn-group btn-group-toggle mr-2" ngbRadioGroup [(ngModel)]="displayMode"
(ngModelChange)="saveDisplayMode()">
@@ -21,14 +21,13 @@
</svg>
</label>
</div>
<div class="btn-group btn-group-toggle mr-2" ngbRadioGroup [(ngModel)]="docs.currentSortDirection"
(ngModelChange)="reload()"
*ngIf="!docs.viewConfig">
<div class="btn-group btn-group-toggle mr-2" ngbRadioGroup [(ngModel)]="docs.sortDirection"
*ngIf="!docs.viewId">
<div ngbDropdown class="btn-group">
<button class="btn btn-outline-secondary btn-sm" id="dropdownBasic1" ngbDropdownToggle>Sort by</button>
<div ngbDropdownMenu aria-labelledby="dropdownBasic1">
<button *ngFor="let f of getSortFields()" ngbDropdownItem (click)="setSort(f.field)"
[class.active]="docs.currentSortField == f.field">{{f.name}}</button>
[class.active]="docs.sortField == f.field">{{f.name}}</button>
</div>
</div>
<label ngbButtonLabel class="btn-outline-secondary btn-sm">
@@ -44,7 +43,7 @@
</svg>
</label>
</div>
<div class="btn-group" *ngIf="!docs.viewConfig">
<div class="btn-group" *ngIf="!docs.viewId">
<button type="button" class="btn btn-sm btn-outline-secondary" (click)="showFilter=!showFilter">
<svg class="toolbaricon" fill="currentColor">
@@ -62,7 +61,6 @@
</div>
</div>
</div>
</app-page-header>

View File

@@ -26,13 +26,16 @@ export class DocumentListComponent implements OnInit {
filterRules: FilterRule[] = []
showFilter = false
getTitle() {
return this.docs.viewConfigOverride ? this.docs.viewConfigOverride.title : "Documents"
}
getSortFields() {
return DOCUMENT_SORT_FIELDS
}
setSort(field: string) {
this.docs.currentSortField = field
this.reload()
this.docs.sortField = field
}
saveDisplayMode() {
@@ -45,11 +48,11 @@ export class DocumentListComponent implements OnInit {
}
this.route.paramMap.subscribe(params => {
if (params.has('id')) {
this.docs.viewConfig = this.savedViewConfigService.getConfig(params.get('id'))
this.docs.viewConfigOverride = this.savedViewConfigService.getConfig(params.get('id'))
} else {
this.filterRules = cloneFilterRules(this.docs.currentFilterRules)
this.filterRules = this.docs.filterRules
this.showFilter = this.filterRules.length > 0
this.docs.viewConfig = null
this.docs.viewConfigOverride = null
}
this.reload()
})
@@ -60,28 +63,24 @@ export class DocumentListComponent implements OnInit {
}
applyFilterRules() {
this.docs.setFilterRules(this.filterRules)
this.reload()
this.docs.filterRules = this.filterRules
}
loadViewConfig(config: SavedViewConfig) {
this.filterRules = cloneFilterRules(config.filterRules)
this.docs.setFilterRules(config.filterRules)
this.docs.currentSortField = config.sortField
this.docs.currentSortDirection = config.sortDirection
this.reload()
this.docs.loadViewConfig(config)
}
saveViewConfig() {
let modal = this.modalService.open(SaveViewConfigDialogComponent, {backdrop: 'static'})
modal.componentInstance.saveClicked.subscribe(formValue => {
this.savedViewConfigService.saveConfig({
filterRules: cloneFilterRules(this.filterRules),
title: formValue.title,
showInDashboard: formValue.showInDashboard,
showInSideBar: formValue.showInSideBar,
sortDirection: this.docs.currentSortDirection,
sortField: this.docs.currentSortField
filterRules: this.docs.filterRules,
sortDirection: this.docs.sortDirection,
sortField: this.docs.sortField
})
modal.close()
})

View File

@@ -1,44 +0,0 @@
.form-signin-container {
top: 0;
bottom: 0;
left: 0;
right: 0;
position: fixed;
background-color: #f5f5f5;
}
.form-signin {
max-width: 330px;
height: auto;
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
margin: auto;
text-align: center;
}
.form-signin .checkbox {
font-weight: 400;
}
.form-signin .form-control {
position: relative;
box-sizing: border-box;
height: auto;
padding: 10px;
font-size: 16px;
}
.form-signin .form-control:focus {
z-index: 2;
}
.form-signin input[type="text"] {
margin-bottom: -1px;
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
}
.form-signin input[type="password"] {
margin-bottom: 10px;
border-top-left-radius: 0;
border-top-right-radius: 0;
}

View File

@@ -1,17 +0,0 @@
<div class="form-signin-container">
<form class="form-signin mt-5" [formGroup]="loginForm" (ngSubmit)="loginClicked()">
<img class="mb-4" src="assets/logo.svg" alt="" width="100%">
<h1 class="h3 mb-3 font-weight-normal">Login</h1>
<label for="inputUsername" class="sr-only">Username</label>
<input type="text" id="inputUsername" class="form-control" placeholder="Username" required autofocus formControlName="username">
<label for="inputPassword" class="sr-only">Password</label>
<input type="password" id="inputPassword" class="form-control" placeholder="Password" required formControlName="password">
<div class="checkbox mb-3">
<label>
<input type="checkbox" value="remember-me" formControlName="rememberMe"> Remember me
</label>
</div>
<button class="btn btn-lg btn-primary btn-block mb-4" type="submit">Login</button>
<p><a href="/admin/">Go to admin interface</a></p>
</form>
</div>

View File

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

View File

@@ -1,34 +0,0 @@
import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms';
import { Router } from '@angular/router';
import { AuthService } from 'src/app/services/auth.service';
import { ToastService } from 'src/app/services/toast.service';
@Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {
constructor(private auth: AuthService, private router: Router, private toastService: ToastService) { }
loginForm = new FormGroup({
username: new FormControl(''),
password: new FormControl(''),
rememberMe: new FormControl(false)
})
ngOnInit(): void {
}
loginClicked() {
this.auth.login(this.loginForm.value.username, this.loginForm.value.password, this.loginForm.value.rememberMe).subscribe(result => {
this.router.navigate([''])
}, (error) => {
this.toastService.showError("Unable to log in with provided credentials.")
}
)
}
}

View File

@@ -9,10 +9,10 @@
<table class="table table-striped">
<thead>
<tr>
<th scope="col">Name</th>
<th scope="col">Matching</th>
<th scope="col">Document count</th>
<th scope="col">Last correspondence</th>
<th scope="col" sortable="name" (sort)="onSort($event)">Name</th>
<th scope="col" sortable="matching_algorithm" (sort)="onSort($event)">Matching</th>
<th scope="col" sortable="document_count" (sort)="onSort($event)">Document count</th>
<th scope="col" sortable="last_correspondence" (sort)="onSort($event)">Last correspondence</th>
<th scope="col">Actions</th>
</tr>
</thead>

View File

@@ -10,9 +10,9 @@
<table class="table table-striped">
<thead>
<tr>
<th scope="col">Name</th>
<th scope="col">Matching</th>
<th scope="col">Document count</th>
<th scope="col" sortable="name" (sort)="onSort($event)">Name</th>
<th scope="col" sortable="matching_algorithm" (sort)="onSort($event)">Matching</th>
<th scope="col" sortable="document_count" (sort)="onSort($event)">Document count</th>
<th scope="col">Actions</th>
</tr>
</thead>

View File

@@ -1,7 +1,8 @@
import { Directive, OnInit } from '@angular/core';
import { Directive, OnInit, QueryList, ViewChildren } from '@angular/core';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { MatchingModel, MATCHING_ALGORITHMS, MATCH_AUTO } from 'src/app/data/matching-model';
import { ObjectWithId } from 'src/app/data/object-with-id';
import { SortableDirective, SortEvent } from 'src/app/directives/sortable.directive';
import { AbstractPaperlessService } from 'src/app/services/rest/abstract-paperless-service';
import { DeleteDialogComponent } from '../../common/delete-dialog/delete-dialog.component';
@@ -14,12 +15,17 @@ export abstract class GenericListComponent<T extends ObjectWithId> implements On
private editDialogComponent: any) {
}
@ViewChildren(SortableDirective) headers: QueryList<SortableDirective>;
public data: T[] = []
public page = 1
public collectionSize = 0
public sortField: string
public sortDirection: string
getMatching(o: MatchingModel) {
if (o.matching_algorithm == MATCH_AUTO) {
return "Automatic"
@@ -30,12 +36,31 @@ export abstract class GenericListComponent<T extends ObjectWithId> implements On
}
}
onSort(event: SortEvent) {
if (event.direction && event.direction.length > 0) {
this.sortField = event.column
this.sortDirection = event.direction
} else {
this.sortField = null
this.sortDirection = null
}
this.headers.forEach(header => {
if (header.sortable !== this.sortField) {
header.direction = '';
}
});
this.reloadData()
}
ngOnInit(): void {
this.reloadData()
}
reloadData() {
this.service.list(this.page).subscribe(c => {
this.service.list(this.page, null, this.sortField, this.sortDirection).subscribe(c => {
this.data = c.results
this.collectionSize = c.count
});

View File

@@ -20,7 +20,7 @@ export class LogsComponent implements OnInit {
}
reload() {
this.logService.list(1, 50, null, {'level__gte': this.level}).subscribe(result => this.logs = result.results)
this.logService.list(1, 50, 'created', 'des', {'level__gte': this.level}).subscribe(result => this.logs = result.results)
}
getLevelText(level: number) {
@@ -32,7 +32,7 @@ export class LogsComponent implements OnInit {
if (this.logs.length > 0) {
lastCreated = this.logs[this.logs.length-1].created
}
this.logService.list(1, 25, null, {'created__lt': lastCreated, 'level__gte': this.level}).subscribe(result => {
this.logService.list(1, 25, 'created', 'des', {'created__lt': lastCreated, 'level__gte': this.level}).subscribe(result => {
this.logs.push(...result.results)
})
}

View File

@@ -34,7 +34,7 @@
<a ngbNavLink>Saved views</a>
<ng-template ngbNavContent>
<table class="table table-striped">
<table class="table table-borderless table-sm">
<thead>
<tr>
<th scope="col">Title</th>
@@ -57,7 +57,7 @@
</li>
</ul>
<div [ngbNavOutlet]="nav" class="mt-2"></div>
<div [ngbNavOutlet]="nav" class="border-left border-right border-bottom p-3 mb-3"></div>
<button type="submit" class="btn btn-primary">Save</button>
</form>

View File

@@ -9,10 +9,10 @@
<table class="table table-striped">
<thead>
<tr>
<th scope="col">Name</th>
<th scope="col" sortable="name" (sort)="onSort($event)">Name</th>
<th scope="col">Colour</th>
<th scope="col">Matching</th>
<th scope="col">Document count</th>
<th scope="col" sortable="matching_algorithm" (sort)="onSort($event)">Matching</th>
<th scope="col" sortable="document_count" (sort)="onSort($event)">Document count</th>
<th scope="col">Actions</th>
</tr>
</thead>