many changes to support server side saved views

This commit is contained in:
jonaswinkler 2020-12-14 19:26:36 +01:00
parent 13d934dc6e
commit b7126030d1
17 changed files with 131 additions and 117 deletions

View File

@ -37,16 +37,16 @@
</li> </li>
</ul> </ul>
<h6 class="sidebar-heading d-flex justify-content-between align-items-center px-3 mt-4 mb-1 text-muted" *ngIf='viewConfigService.getSideBarConfigs().length > 0'> <h6 class="sidebar-heading d-flex justify-content-between align-items-center px-3 mt-4 mb-1 text-muted" *ngIf='savedViewService.sidebarViews.length > 0'>
<span>Saved views</span> <span>Saved views</span>
</h6> </h6>
<ul class="nav flex-column mb-2"> <ul class="nav flex-column mb-2">
<li class="nav-item w-100" *ngFor='let config of viewConfigService.getSideBarConfigs()'> <li class="nav-item w-100" *ngFor="let view of savedViewService.sidebarViews">
<a class="nav-link text-truncate" routerLink="view/{{config.id}}" routerLinkActive="active" (click)="closeMenu()"> <a class="nav-link text-truncate" routerLink="view/{{view.id}}" routerLinkActive="active" (click)="closeMenu()">
<svg class="sidebaricon" fill="currentColor"> <svg class="sidebaricon" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#funnel"/> <use xlink:href="assets/bootstrap-icons.svg#funnel"/>
</svg> </svg>
{{config.title}} {{view.name}}
</a> </a>
</li> </li>
</ul> </ul>

View File

@ -5,8 +5,8 @@ import { from, Observable, Subscription } from 'rxjs';
import { debounceTime, distinctUntilChanged, map, switchMap } from 'rxjs/operators'; import { debounceTime, distinctUntilChanged, map, switchMap } from 'rxjs/operators';
import { PaperlessDocument } from 'src/app/data/paperless-document'; import { PaperlessDocument } from 'src/app/data/paperless-document';
import { OpenDocumentsService } from 'src/app/services/open-documents.service'; import { OpenDocumentsService } from 'src/app/services/open-documents.service';
import { SavedViewService } from 'src/app/services/rest/saved-view.service';
import { SearchService } from 'src/app/services/rest/search.service'; import { SearchService } from 'src/app/services/rest/search.service';
import { SavedViewConfigService } from 'src/app/services/saved-view-config.service';
import { DocumentDetailComponent } from '../document-detail/document-detail.component'; import { DocumentDetailComponent } from '../document-detail/document-detail.component';
@Component({ @Component({
@ -21,7 +21,7 @@ export class AppFrameComponent implements OnInit, OnDestroy {
private activatedRoute: ActivatedRoute, private activatedRoute: ActivatedRoute,
private openDocumentsService: OpenDocumentsService, private openDocumentsService: OpenDocumentsService,
private searchService: SearchService, private searchService: SearchService,
public viewConfigService: SavedViewConfigService public savedViewService: SavedViewService
) { ) {
} }

View File

@ -1,6 +1,7 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { Title } from '@angular/platform-browser'; import { Title } from '@angular/platform-browser';
import { SavedViewConfigService } from 'src/app/services/saved-view-config.service'; import { PaperlessSavedView } from 'src/app/data/paperless-saved-view';
import { SavedViewService } from 'src/app/services/rest/saved-view.service';
import { environment } from 'src/environments/environment'; import { environment } from 'src/environments/environment';
@ -12,14 +13,16 @@ import { environment } from 'src/environments/environment';
export class DashboardComponent implements OnInit { export class DashboardComponent implements OnInit {
constructor( constructor(
public savedViewConfigService: SavedViewConfigService, private savedViewService: SavedViewService,
private titleService: Title) { } private titleService: Title) { }
savedViews = [] savedViews: PaperlessSavedView[] = []
ngOnInit(): void { ngOnInit(): void {
this.savedViews = this.savedViewConfigService.getDashboardConfigs() this.savedViewService.listAll().subscribe(results => {
this.savedViews = results.results.filter(savedView => savedView.show_on_dashboard)
})
this.titleService.setTitle(`Dashboard - ${environment.appTitle}`) this.titleService.setTitle(`Dashboard - ${environment.appTitle}`)
} }

View File

@ -1,4 +1,4 @@
<app-widget-frame [title]="savedView.title"> <app-widget-frame [title]="savedView.name">
<a header-buttons [routerLink]="" (click)="showAll()">Show all</a> <a header-buttons [routerLink]="" (click)="showAll()">Show all</a>

View File

@ -1,7 +1,7 @@
import { Component, Input, OnInit } from '@angular/core'; import { Component, Input, OnInit } from '@angular/core';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { PaperlessDocument } from 'src/app/data/paperless-document'; import { PaperlessDocument } from 'src/app/data/paperless-document';
import { SavedViewConfig } from 'src/app/data/saved-view-config'; import { PaperlessSavedView } from 'src/app/data/paperless-saved-view';
import { DocumentListViewService } from 'src/app/services/document-list-view.service'; import { DocumentListViewService } from 'src/app/services/document-list-view.service';
import { DocumentService } from 'src/app/services/rest/document.service'; import { DocumentService } from 'src/app/services/rest/document.service';
@ -18,18 +18,18 @@ export class SavedViewWidgetComponent implements OnInit {
private list: DocumentListViewService) { } private list: DocumentListViewService) { }
@Input() @Input()
savedView: SavedViewConfig savedView: PaperlessSavedView
documents: PaperlessDocument[] = [] documents: PaperlessDocument[] = []
ngOnInit(): void { ngOnInit(): void {
this.documentService.list(1,10,this.savedView.sortField,this.savedView.sortDirection,this.savedView.filterRules).subscribe(result => { this.documentService.list(1,10,this.savedView.sort_field, this.savedView.sort_reverse, this.savedView.filter_rules).subscribe(result => {
this.documents = result.results this.documents = result.results
}) })
} }
showAll() { showAll() {
if (this.savedView.showInSideBar) { if (this.savedView.show_in_sidebar) {
this.router.navigate(['view', this.savedView.id]) this.router.navigate(['view', this.savedView.id])
} else { } else {
this.list.load(this.savedView) this.list.load(this.savedView)

View File

@ -21,7 +21,7 @@
</label> </label>
</div> </div>
<div class="btn-group btn-group-toggle ml-2" ngbRadioGroup [(ngModel)]="list.sortDirection"> <div class="btn-group btn-group-toggle ml-2" ngbRadioGroup [(ngModel)]="list.sortReverse">
<div ngbDropdown class="btn-group"> <div ngbDropdown class="btn-group">
<button class="btn btn-outline-primary btn-sm" id="dropdownBasic1" ngbDropdownToggle>Sort by</button> <button class="btn btn-outline-primary btn-sm" id="dropdownBasic1" ngbDropdownToggle>Sort by</button>
<div ngbDropdownMenu aria-labelledby="dropdownBasic1" class="shadow"> <div ngbDropdownMenu aria-labelledby="dropdownBasic1" class="shadow">
@ -30,13 +30,13 @@
</div> </div>
</div> </div>
<label ngbButtonLabel class="btn-outline-primary btn-sm"> <label ngbButtonLabel class="btn-outline-primary btn-sm">
<input ngbButton type="radio" class="btn btn-sm" value="asc"> <input ngbButton type="radio" class="btn btn-sm" [value]="false">
<svg class="toolbaricon" fill="currentColor"> <svg class="toolbaricon" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#sort-alpha-down" /> <use xlink:href="assets/bootstrap-icons.svg#sort-alpha-down" />
</svg> </svg>
</label> </label>
<label ngbButtonLabel class="btn-outline-primary btn-sm"> <label ngbButtonLabel class="btn-outline-primary btn-sm">
<input ngbButton type="radio" class="btn btn-sm" value="des"> <input ngbButton type="radio" class="btn btn-sm" [value]="true">
<svg class="toolbaricon" fill="currentColor"> <svg class="toolbaricon" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#sort-alpha-up-alt" /> <use xlink:href="assets/bootstrap-icons.svg#sort-alpha-up-alt" />
</svg> </svg>
@ -49,8 +49,8 @@
<button class="btn btn-sm btn-outline-primary dropdown-toggle" ngbDropdownToggle>Views</button> <button class="btn btn-sm btn-outline-primary dropdown-toggle" ngbDropdownToggle>Views</button>
<div class="dropdown-menu shadow" ngbDropdownMenu> <div class="dropdown-menu shadow" ngbDropdownMenu>
<ng-container *ngIf="!list.savedViewId"> <ng-container *ngIf="!list.savedViewId">
<button ngbDropdownItem *ngFor="let config of savedViewConfigService.getConfigs()" (click)="loadViewConfig(config)">{{config.title}}</button> <button ngbDropdownItem *ngFor="let view of savedViewService.allViews" (click)="loadViewConfig(view)">{{view.name}}</button>
<div class="dropdown-divider" *ngIf="savedViewConfigService.getConfigs().length > 0"></div> <div class="dropdown-divider" *ngIf="savedViewService.allViews.length > 0"></div>
</ng-container> </ng-container>
<button ngbDropdownItem (click)="saveViewConfig()" *ngIf="list.savedViewId">Save "{{list.savedViewTitle}}"</button> <button ngbDropdownItem (click)="saveViewConfig()" *ngIf="list.savedViewId">Save "{{list.savedViewTitle}}"</button>

View File

@ -1,15 +1,16 @@
import { Component, OnInit, ViewChild } from '@angular/core'; import { Component, OnInit, ViewChild } from '@angular/core';
import { Title } from '@angular/platform-browser'; import { Title } from '@angular/platform-browser';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { SavedViewConfig } from 'src/app/data/saved-view-config'; import { PaperlessSavedView } from 'src/app/data/paperless-saved-view';
import { DocumentListViewService } from 'src/app/services/document-list-view.service'; import { DocumentListViewService } from 'src/app/services/document-list-view.service';
import { DOCUMENT_SORT_FIELDS } from 'src/app/services/rest/document.service'; import { DOCUMENT_SORT_FIELDS } from 'src/app/services/rest/document.service';
import { SavedViewConfigService } from 'src/app/services/saved-view-config.service'; import { SavedViewService } from 'src/app/services/rest/saved-view.service';
import { Toast, ToastService } from 'src/app/services/toast.service'; import { Toast, ToastService } from 'src/app/services/toast.service';
import { environment } from 'src/environments/environment'; import { environment } from 'src/environments/environment';
import { FilterEditorComponent } from '../filter-editor/filter-editor.component'; import { FilterEditorComponent } from '../filter-editor/filter-editor.component';
import { SaveViewConfigDialogComponent } from './save-view-config-dialog/save-view-config-dialog.component'; import { SaveViewConfigDialogComponent } from './save-view-config-dialog/save-view-config-dialog.component';
@Component({ @Component({
selector: 'app-document-list', selector: 'app-document-list',
templateUrl: './document-list.component.html', templateUrl: './document-list.component.html',
@ -19,8 +20,9 @@ export class DocumentListComponent implements OnInit {
constructor( constructor(
public list: DocumentListViewService, public list: DocumentListViewService,
public savedViewConfigService: SavedViewConfigService, public savedViewService: SavedViewService,
public route: ActivatedRoute, public route: ActivatedRoute,
private router: Router,
private toastService: ToastService, private toastService: ToastService,
public modalService: NgbModal, public modalService: NgbModal,
private titleService: Title) { } private titleService: Title) { }
@ -51,40 +53,51 @@ export class DocumentListComponent implements OnInit {
this.displayMode = localStorage.getItem('document-list:displayMode') this.displayMode = localStorage.getItem('document-list:displayMode')
} }
this.route.paramMap.subscribe(params => { this.route.paramMap.subscribe(params => {
this.list.clear()
if (params.has('id')) { if (params.has('id')) {
this.list.savedView = this.savedViewConfigService.getConfig(params.get('id')) this.savedViewService.getCached(+params.get('id')).subscribe(view => {
this.titleService.setTitle(`${this.list.savedView.title} - ${environment.appTitle}`) if (!view) {
this.router.navigate(["404"])
return
}
this.list.savedView = view
this.titleService.setTitle(`${this.list.savedView.name} - ${environment.appTitle}`)
this.list.reload()
})
} else { } else {
this.list.savedView = null this.list.savedView = null
this.titleService.setTitle(`Documents - ${environment.appTitle}`) this.titleService.setTitle(`Documents - ${environment.appTitle}`)
this.list.reload()
} }
this.list.clear()
this.list.reload()
}) })
} }
loadViewConfig(config: SavedViewConfig) { loadViewConfig(view: PaperlessSavedView) {
this.list.load(config) this.list.load(view)
} }
saveViewConfig() { saveViewConfig() {
this.savedViewConfigService.updateConfig(this.list.savedView) this.savedViewService.update(this.list.savedView).subscribe(result => {
this.toastService.showToast(Toast.make("Information", `View "${this.list.savedView.title}" saved successfully.`)) this.toastService.showToast(Toast.make("Information", `View "${this.list.savedView.name}" saved successfully.`))
})
} }
saveViewConfigAs() { saveViewConfigAs() {
let modal = this.modalService.open(SaveViewConfigDialogComponent, {backdrop: 'static'}) let modal = this.modalService.open(SaveViewConfigDialogComponent, {backdrop: 'static'})
modal.componentInstance.saveClicked.subscribe(formValue => { modal.componentInstance.saveClicked.subscribe(formValue => {
this.savedViewConfigService.newConfig({ this.savedViewService.create({
title: formValue.title, name: formValue.name,
showInDashboard: formValue.showInDashboard, show_on_dashboard: formValue.showOnDashboard,
showInSideBar: formValue.showInSideBar, show_in_sidebar: formValue.showInSideBar,
filterRules: this.list.filterRules, filter_rules: this.list.filterRules,
sortDirection: this.list.sortDirection, sort_reverse: this.list.sortReverse,
sortField: this.list.sortField sort_field: this.list.sortField
}).subscribe(() => {
modal.close()
}) })
modal.close()
}) })
} }

View File

@ -6,9 +6,9 @@
</button> </button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<app-input-text title="Title" formControlName="title"></app-input-text> <app-input-text title="Name" formControlName="name"></app-input-text>
<app-input-check title="Show in side bar" formControlName="showInSideBar"></app-input-check> <app-input-check title="Show in side bar" formControlName="showInSideBar"></app-input-check>
<app-input-check title="Show in dashboard" formControlName="showInDashboard"></app-input-check> <app-input-check title="Show on dashboard" formControlName="showOnDashboard"></app-input-check>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-outline-dark" (click)="cancel()">Cancel</button> <button type="button" class="btn btn-outline-dark" (click)="cancel()">Cancel</button>

View File

@ -15,9 +15,9 @@ export class SaveViewConfigDialogComponent implements OnInit {
public saveClicked = new EventEmitter() public saveClicked = new EventEmitter()
saveViewConfigForm = new FormGroup({ saveViewConfigForm = new FormGroup({
title: new FormControl(''), name: new FormControl(''),
showInSideBar: new FormControl(false), showInSideBar: new FormControl(false),
showInDashboard: new FormControl(false), showOnDashboard: new FormControl(false),
}) })
ngOnInit(): void { ngOnInit(): void {

View File

@ -39,22 +39,22 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
} }
get selectedTags(): PaperlessTag[] { get selectedTags(): PaperlessTag[] {
let tagRules: FilterRule[] = this.filterRules.filter(fr => fr.type.id == FILTER_HAS_TAG) let tagRules: FilterRule[] = this.filterRules.filter(fr => fr.rule_type == FILTER_HAS_TAG)
return this.tags?.filter(t => tagRules.find(tr => tr.value == t.id)) return this.tags?.filter(t => tagRules.find(tr => tr.value == t.id))
} }
get selectedCorrespondents(): PaperlessCorrespondent[] { get selectedCorrespondents(): PaperlessCorrespondent[] {
let correspondentRules: FilterRule[] = this.filterRules.filter(fr => fr.type.id == FILTER_CORRESPONDENT) let correspondentRules: FilterRule[] = this.filterRules.filter(fr => fr.rule_type == FILTER_CORRESPONDENT)
return this.correspondents?.filter(c => correspondentRules.find(cr => cr.value == c.id)) return this.correspondents?.filter(c => correspondentRules.find(cr => cr.value == c.id))
} }
get selectedDocumentTypes(): PaperlessDocumentType[] { get selectedDocumentTypes(): PaperlessDocumentType[] {
let documentTypeRules: FilterRule[] = this.filterRules.filter(fr => fr.type.id == FILTER_DOCUMENT_TYPE) let documentTypeRules: FilterRule[] = this.filterRules.filter(fr => fr.rule_type == FILTER_DOCUMENT_TYPE)
return this.documentTypes?.filter(dt => documentTypeRules.find(dtr => dtr.value == dt.id)) return this.documentTypes?.filter(dt => documentTypeRules.find(dtr => dtr.value == dt.id))
} }
get titleFilter() { get titleFilter() {
let existingRule = this.filterRules.find(rule => rule.type.id == FILTER_TITLE) let existingRule = this.filterRules.find(rule => rule.rule_type == FILTER_TITLE)
return existingRule ? existingRule.value : '' return existingRule ? existingRule.value : ''
} }
@ -100,15 +100,15 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
let filterRuleType = FILTER_RULE_TYPES.find(t => t.id == filterRuleTypeID) let filterRuleType = FILTER_RULE_TYPES.find(t => t.id == filterRuleTypeID)
let existingRule = this.filterRules.find(rule => rule.type.id == filterRuleTypeID && rule.value == value) let existingRule = this.filterRules.find(rule => rule.rule_type == filterRuleTypeID && rule.value == value)
let existingRuleOfSameType = this.filterRules.find(rule => rule.type.id == filterRuleTypeID) let existingRuleOfSameType = this.filterRules.find(rule => rule.rule_type == filterRuleTypeID)
if (existingRule) { if (existingRule) {
// if this exact rule already exists, remove it in all cases. // if this exact rule already exists, remove it in all cases.
this.filterRules.splice(this.filterRules.indexOf(existingRule), 1) this.filterRules.splice(this.filterRules.indexOf(existingRule), 1)
} else if (filterRuleType.multi || !existingRuleOfSameType) { } else if (filterRuleType.multi || !existingRuleOfSameType) {
// if we allow multiple rules per type, or no rule of this type already exists, push a new rule. // if we allow multiple rules per type, or no rule of this type already exists, push a new rule.
this.filterRules.push({type: filterRuleType, value: value}) this.filterRules.push({rule_type: filterRuleTypeID, value: value})
} else { } else {
// otherwise (i.e., no multi support AND there's already a rule of this type), update the rule. // otherwise (i.e., no multi support AND there's already a rule of this type), update the rule.
existingRuleOfSameType.value = value existingRuleOfSameType.value = value
@ -117,12 +117,12 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
} }
private setTitleRule(title: string) { private setTitleRule(title: string) {
let existingRule = this.filterRules.find(rule => rule.type.id == FILTER_TITLE) let existingRule = this.filterRules.find(rule => rule.rule_type == FILTER_TITLE)
if (!existingRule && title) { if (!existingRule && title) {
this.filterRules.push({type: FILTER_RULE_TYPES.find(t => t.id == FILTER_TITLE), value: title}) this.filterRules.push({rule_type: FILTER_TITLE, value: title})
} else if (existingRule && !title) { } else if (existingRule && !title) {
this.filterRules.splice(this.filterRules.findIndex(rule => rule.type.id == FILTER_TITLE), 1) this.filterRules.splice(this.filterRules.findIndex(rule => rule.rule_type == FILTER_TITLE), 1)
} else if (existingRule && title) { } else if (existingRule && title) {
existingRule.value = title existingRule.value = title
} }
@ -167,7 +167,7 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
} }
get dateCreatedBefore(): NgbDateStruct { get dateCreatedBefore(): NgbDateStruct {
let createdBeforeRule: FilterRule = this.filterRules.find(fr => fr.type.id == FILTER_CREATED_BEFORE) let createdBeforeRule: FilterRule = this.filterRules.find(fr => fr.rule_type == FILTER_CREATED_BEFORE)
return createdBeforeRule ? { return createdBeforeRule ? {
year: createdBeforeRule.value.substring(0,4), year: createdBeforeRule.value.substring(0,4),
month: createdBeforeRule.value.substring(5,7), month: createdBeforeRule.value.substring(5,7),
@ -176,7 +176,7 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
} }
get dateCreatedAfter(): NgbDateStruct { get dateCreatedAfter(): NgbDateStruct {
let createdAfterRule: FilterRule = this.filterRules.find(fr => fr.type.id == FILTER_CREATED_AFTER) let createdAfterRule: FilterRule = this.filterRules.find(fr => fr.rule_type == FILTER_CREATED_AFTER)
return createdAfterRule ? { return createdAfterRule ? {
year: createdAfterRule.value.substring(0,4), year: createdAfterRule.value.substring(0,4),
month: createdAfterRule.value.substring(5,7), month: createdAfterRule.value.substring(5,7),
@ -185,7 +185,7 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
} }
get dateAddedBefore(): NgbDateStruct { get dateAddedBefore(): NgbDateStruct {
let addedBeforeRule: FilterRule = this.filterRules.find(fr => fr.type.id == FILTER_ADDED_BEFORE) let addedBeforeRule: FilterRule = this.filterRules.find(fr => fr.rule_type == FILTER_ADDED_BEFORE)
return addedBeforeRule ? { return addedBeforeRule ? {
year: addedBeforeRule.value.substring(0,4), year: addedBeforeRule.value.substring(0,4),
month: addedBeforeRule.value.substring(5,7), month: addedBeforeRule.value.substring(5,7),
@ -194,7 +194,7 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
} }
get dateAddedAfter(): NgbDateStruct { get dateAddedAfter(): NgbDateStruct {
let addedAfterRule: FilterRule = this.filterRules.find(fr => fr.type.id == FILTER_ADDED_AFTER) let addedAfterRule: FilterRule = this.filterRules.find(fr => fr.rule_type == FILTER_ADDED_AFTER)
return addedAfterRule ? { return addedAfterRule ? {
year: addedAfterRule.value.substring(0,4), year: addedAfterRule.value.substring(0,4),
month: addedAfterRule.value.substring(5,7), month: addedAfterRule.value.substring(5,7),
@ -224,13 +224,13 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
setDateFilter(date: NgbDateStruct, dateRuleTypeID: number) { setDateFilter(date: NgbDateStruct, dateRuleTypeID: number) {
let filterRules = this.filterRules let filterRules = this.filterRules
let existingRule = filterRules.find(rule => rule.type.id == dateRuleTypeID) let existingRule = filterRules.find(rule => rule.rule_type == dateRuleTypeID)
let newValue = `${date.year}-${date.month.toString().padStart(2,'0')}-${date.day.toString().padStart(2,'0')}` // YYYY-MM-DD let newValue = `${date.year}-${date.month.toString().padStart(2,'0')}-${date.day.toString().padStart(2,'0')}` // YYYY-MM-DD
if (existingRule) { if (existingRule) {
existingRule.value = newValue existingRule.value = newValue
} else { } else {
filterRules.push({type: FILTER_RULE_TYPES.find(t => t.id == dateRuleTypeID), value: newValue}) filterRules.push({rule_type: dateRuleTypeID, value: newValue})
} }
this.filterRules = filterRules this.filterRules = filterRules
@ -238,7 +238,7 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
clearDateFilter(dateRuleTypeID: number) { clearDateFilter(dateRuleTypeID: number) {
let filterRules = this.filterRules let filterRules = this.filterRules
let existingRule = filterRules.find(rule => rule.type.id == dateRuleTypeID) let existingRule = filterRules.find(rule => rule.rule_type == dateRuleTypeID)
filterRules.splice(filterRules.indexOf(existingRule), 1) filterRules.splice(filterRules.indexOf(existingRule), 1)
this.filterRules = filterRules this.filterRules = filterRules
} }

View File

@ -60,7 +60,8 @@ export abstract class GenericListComponent<T extends ObjectWithId> implements On
} }
reloadData() { reloadData() {
this.service.list(this.page, null, this.sortField, this.sortDirection).subscribe(c => { // TODO: this is a hack
this.service.list(this.page, null, this.sortField, this.sortDirection == 'des').subscribe(c => {
this.data = c.results this.data = c.results
this.collectionSize = c.count this.collectionSize = c.count
}); });

View File

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

View File

@ -44,11 +44,11 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr *ngFor="let config of savedViewConfigService.getConfigs()"> <tr *ngFor="let view of savedViewService.allViews">
<td>{{ config.title }}</td> <td>{{ view.name }}</td>
<td>{{ config.showInDashboard | yesno }}</td> <td>{{ view.show_on_dashboard | yesno }}</td>
<td>{{ config.showInSideBar | yesno }}</td> <td>{{ view.show_in_sidebar | yesno }}</td>
<td><button type="button" class="btn btn-sm btn-outline-danger" (click)="deleteViewConfig(config)">Delete</button></td> <td><button type="button" class="btn btn-sm btn-outline-danger" (click)="deleteSavedView(view)">Delete</button></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>

View File

@ -1,10 +1,11 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms'; import { FormControl, FormGroup } from '@angular/forms';
import { Title } from '@angular/platform-browser'; import { Title } from '@angular/platform-browser';
import { SavedViewConfig } from 'src/app/data/saved-view-config'; import { map, tap } from 'rxjs/operators';
import { PaperlessSavedView } from 'src/app/data/paperless-saved-view';
import { GENERAL_SETTINGS } from 'src/app/data/storage-keys'; import { GENERAL_SETTINGS } from 'src/app/data/storage-keys';
import { DocumentListViewService } from 'src/app/services/document-list-view.service'; import { DocumentListViewService } from 'src/app/services/document-list-view.service';
import { SavedViewConfigService } from 'src/app/services/saved-view-config.service'; import { SavedViewService } from 'src/app/services/rest/saved-view.service';
import { environment } from 'src/environments/environment'; import { environment } from 'src/environments/environment';
@Component({ @Component({
@ -19,7 +20,7 @@ export class SettingsComponent implements OnInit {
}) })
constructor( constructor(
private savedViewConfigService: SavedViewConfigService, public savedViewService: SavedViewService,
private documentListViewService: DocumentListViewService, private documentListViewService: DocumentListViewService,
private titleService: Title private titleService: Title
) { } ) { }
@ -28,8 +29,8 @@ export class SettingsComponent implements OnInit {
this.titleService.setTitle(`Settings - ${environment.appTitle}`) this.titleService.setTitle(`Settings - ${environment.appTitle}`)
} }
deleteViewConfig(config: SavedViewConfig) { deleteSavedView(savedView: PaperlessSavedView) {
this.savedViewConfigService.deleteConfig(config) this.savedViewService.delete(savedView).subscribe(() => {})
} }
saveSettings() { saveSettings() {

View File

@ -2,7 +2,7 @@ import { Injectable } from '@angular/core';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { cloneFilterRules, FilterRule } from '../data/filter-rule'; import { cloneFilterRules, FilterRule } from '../data/filter-rule';
import { PaperlessDocument } from '../data/paperless-document'; import { PaperlessDocument } from '../data/paperless-document';
import { SavedViewConfig } from '../data/saved-view-config'; import { PaperlessSavedView } from '../data/paperless-saved-view';
import { DOCUMENT_LIST_SERVICE, GENERAL_SETTINGS } from '../data/storage-keys'; import { DOCUMENT_LIST_SERVICE, GENERAL_SETTINGS } from '../data/storage-keys';
import { DocumentService } from './rest/document.service'; import { DocumentService } from './rest/document.service';
@ -29,17 +29,17 @@ export class DocumentListViewService {
/** /**
* This is the current config for the document list. The service will always remember the last settings used for the document list. * This is the current config for the document list. The service will always remember the last settings used for the document list.
*/ */
private _documentListViewConfig: SavedViewConfig private _documentListViewConfig: PaperlessSavedView
/** /**
* Optionally, this is the currently selected saved view, which might be null. * Optionally, this is the currently selected saved view, which might be null.
*/ */
private _savedViewConfig: SavedViewConfig private _savedViewConfig: PaperlessSavedView
get savedView() { get savedView(): PaperlessSavedView {
return this._savedViewConfig return this._savedViewConfig
} }
set savedView(value) { set savedView(value: PaperlessSavedView) {
if (value) { if (value) {
//this is here so that we don't modify value, which might be the actual instance of the saved view. //this is here so that we don't modify value, which might be the actual instance of the saved view.
this._savedViewConfig = Object.assign({}, value) this._savedViewConfig = Object.assign({}, value)
@ -53,7 +53,7 @@ export class DocumentListViewService {
} }
get savedViewTitle() { get savedViewTitle() {
return this.savedView?.title return this.savedView?.name
} }
get documentListView() { get documentListView() {
@ -75,10 +75,11 @@ export class DocumentListViewService {
return this.savedView || this.documentListView return this.savedView || this.documentListView
} }
load(config: SavedViewConfig) { load(view: PaperlessSavedView) {
this.view.filterRules = cloneFilterRules(config.filterRules) this.view.filter_rules = cloneFilterRules(view.filter_rules)
this.view.sortDirection = config.sortDirection this.view.sort_reverse = view.sort_reverse
this.view.sortField = config.sortField this.view.sort_field = view.sort_field
this.saveDocumentListView()
this.reload() this.reload()
} }
@ -93,9 +94,9 @@ export class DocumentListViewService {
this.documentService.list( this.documentService.list(
this.currentPage, this.currentPage,
this.currentPageSize, this.currentPageSize,
this.view.sortField, this.view.sort_field,
this.view.sortDirection, this.view.sort_reverse,
this.view.filterRules).subscribe( this.view.filter_rules).subscribe(
result => { result => {
this.collectionSize = result.count this.collectionSize = result.count
this.documents = result.results this.documents = result.results
@ -116,33 +117,33 @@ export class DocumentListViewService {
set filterRules(filterRules: FilterRule[]) { set filterRules(filterRules: FilterRule[]) {
//we're going to clone the filterRules object, since we don't //we're going to clone the filterRules object, since we don't
//want changes in the filter editor to propagate into here right away. //want changes in the filter editor to propagate into here right away.
this.view.filterRules = cloneFilterRules(filterRules) this.view.filter_rules = cloneFilterRules(filterRules)
this.reload() this.reload()
this.saveDocumentListView() this.saveDocumentListView()
} }
get filterRules(): FilterRule[] { get filterRules(): FilterRule[] {
return cloneFilterRules(this.view.filterRules) return cloneFilterRules(this.view.filter_rules)
} }
set sortField(field: string) { set sortField(field: string) {
this.view.sortField = field this.view.sort_field = field
this.saveDocumentListView() this.saveDocumentListView()
this.reload() this.reload()
} }
get sortField(): string { get sortField(): string {
return this.view.sortField return this.view.sort_field
} }
set sortDirection(direction: string) { set sortReverse(reverse: boolean) {
this.view.sortDirection = direction this.view.sort_reverse = reverse
this.saveDocumentListView() this.saveDocumentListView()
this.reload() this.reload()
} }
get sortDirection(): string { get sortReverse(): boolean {
return this.view.sortDirection return this.view.sort_reverse
} }
private saveDocumentListView() { private saveDocumentListView() {
@ -204,9 +205,9 @@ export class DocumentListViewService {
} }
if (!this.documentListView) { if (!this.documentListView) {
this.documentListView = { this.documentListView = {
filterRules: [], filter_rules: [],
sortDirection: 'des', sort_reverse: true,
sortField: 'created' sort_field: 'created'
} }
} }
} }

View File

@ -22,17 +22,15 @@ export abstract class AbstractPaperlessService<T extends ObjectWithId> {
return url return url
} }
private getOrderingQueryParam(sortField: string, sortDirection: string) { private getOrderingQueryParam(sortField: string, sortReverse: boolean) {
if (sortField && sortDirection) { if (sortField) {
return (sortDirection == 'des' ? '-' : '') + sortField return (sortReverse ? '-' : '') + sortField
} else if (sortField) {
return sortField
} else { } else {
return null return null
} }
} }
list(page?: number, pageSize?: number, sortField?: string, sortDirection?: string, extraParams?): Observable<Results<T>> { list(page?: number, pageSize?: number, sortField?: string, sortReverse?: boolean, extraParams?): Observable<Results<T>> {
let httpParams = new HttpParams() let httpParams = new HttpParams()
if (page) { if (page) {
httpParams = httpParams.set('page', page.toString()) httpParams = httpParams.set('page', page.toString())
@ -40,7 +38,7 @@ export abstract class AbstractPaperlessService<T extends ObjectWithId> {
if (pageSize) { if (pageSize) {
httpParams = httpParams.set('page_size', pageSize.toString()) httpParams = httpParams.set('page_size', pageSize.toString())
} }
let ordering = this.getOrderingQueryParam(sortField, sortDirection) let ordering = this.getOrderingQueryParam(sortField, sortReverse)
if (ordering) { if (ordering) {
httpParams = httpParams.set('ordering', ordering) httpParams = httpParams.set('ordering', ordering)
} }

View File

@ -10,7 +10,7 @@ import { map } from 'rxjs/operators';
import { CorrespondentService } from './correspondent.service'; import { CorrespondentService } from './correspondent.service';
import { DocumentTypeService } from './document-type.service'; import { DocumentTypeService } from './document-type.service';
import { TagService } from './tag.service'; import { TagService } from './tag.service';
import { FILTER_RULE_TYPES } from 'src/app/data/filter-rule-type';
export const DOCUMENT_SORT_FIELDS = [ export const DOCUMENT_SORT_FIELDS = [
{ field: "correspondent__name", name: "Correspondent" }, { field: "correspondent__name", name: "Correspondent" },
@ -22,10 +22,6 @@ export const DOCUMENT_SORT_FIELDS = [
{ field: 'modified', name: 'Modified' } { field: 'modified', name: 'Modified' }
] ]
export const SORT_DIRECTION_ASCENDING = "asc"
export const SORT_DIRECTION_DESCENDING = "des"
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })
@ -39,10 +35,11 @@ export class DocumentService extends AbstractPaperlessService<PaperlessDocument>
if (filterRules) { if (filterRules) {
let params = {} let params = {}
for (let rule of filterRules) { for (let rule of filterRules) {
if (rule.type.multi) { let ruleType = FILTER_RULE_TYPES.find(t => t.id == rule.rule_type)
params[rule.type.filtervar] = params[rule.type.filtervar] ? params[rule.type.filtervar] + "," + rule.value : rule.value if (ruleType.multi) {
params[ruleType.filtervar] = params[ruleType.filtervar] ? params[ruleType.filtervar] + "," + rule.value : rule.value
} else { } else {
params[rule.type.filtervar] = rule.value params[ruleType.filtervar] = rule.value
} }
} }
return params return params
@ -64,8 +61,8 @@ export class DocumentService extends AbstractPaperlessService<PaperlessDocument>
return doc return doc
} }
list(page?: number, pageSize?: number, sortField?: string, sortDirection?: string, filterRules?: FilterRule[]): Observable<Results<PaperlessDocument>> { list(page?: number, pageSize?: number, sortField?: string, sortReverse?: boolean, filterRules?: FilterRule[]): Observable<Results<PaperlessDocument>> {
return super.list(page, pageSize, sortField, sortDirection, this.filterRulesToQueryParams(filterRules)).pipe( return super.list(page, pageSize, sortField, sortReverse, this.filterRulesToQueryParams(filterRules)).pipe(
map(results => { map(results => {
results.results.forEach(doc => this.addObservablesToDocument(doc)) results.results.forEach(doc => this.addObservablesToDocument(doc))
return results return results