refactored filter service

- I wasn't too happy with that in the end.
- The filter editor should not be concerned about managing filter rule state.
- Therefore, it should not access a service for filter rules.
- The editor should simply be given a set of rules, and edit that rule set.
- The only entity that should manage filter state should be the document list service, and the saved view service in the form of filters associated with saved views.
This commit is contained in:
jonaswinkler 2020-12-14 16:51:01 +01:00
parent 10440ec820
commit 94c07839a4
7 changed files with 186 additions and 266 deletions

View File

@ -63,7 +63,7 @@
</app-page-header> </app-page-header>
<div class="w-100 mb-4"> <div class="w-100 mb-4">
<app-filter-editor [(filterEditorService)]="filterEditorService" (apply)="applyFilterRules()" (clear)="clearFilterRules()" #filterEditor></app-filter-editor> <app-filter-editor [(filterRules)]="list.filterRules" #filterEditor></app-filter-editor>
</div> </div>
<div class="d-flex justify-content-between align-items-center"> <div class="d-flex justify-content-between align-items-center">

View File

@ -2,21 +2,14 @@ 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 } from '@angular/router';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { cloneFilterRules, FilterRule } from 'src/app/data/filter-rule';
import { FILTER_CORRESPONDENT, FILTER_DOCUMENT_TYPE, FILTER_HAS_TAG, FILTER_RULE_TYPES } from 'src/app/data/filter-rule-type';
import { SavedViewConfig } from 'src/app/data/saved-view-config'; import { SavedViewConfig } from 'src/app/data/saved-view-config';
import { DocumentListViewService } from 'src/app/services/document-list-view.service'; import { DocumentListViewService } from 'src/app/services/document-list-view.service';
import { FilterEditorViewService } from 'src/app/services/filter-editor-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 { SavedViewConfigService } from 'src/app/services/saved-view-config.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 { SaveViewConfigDialogComponent } from './save-view-config-dialog/save-view-config-dialog.component'; import { SaveViewConfigDialogComponent } from './save-view-config-dialog/save-view-config-dialog.component';
import { FilterEditorComponent } from 'src/app/components/filter-editor/filter-editor.component';
import { PaperlessTag } from 'src/app/data/paperless-tag';
import { PaperlessCorrespondent } from 'src/app/data/paperless-correspondent';
import { PaperlessDocumentType } from 'src/app/data/paperless-document-type';
@Component({ @Component({
selector: 'app-document-list', selector: 'app-document-list',
templateUrl: './document-list.component.html', templateUrl: './document-list.component.html',
@ -27,26 +20,20 @@ export class DocumentListComponent implements OnInit {
constructor( constructor(
public list: DocumentListViewService, public list: DocumentListViewService,
public savedViewConfigService: SavedViewConfigService, public savedViewConfigService: SavedViewConfigService,
public filterEditorService: FilterEditorViewService,
public route: ActivatedRoute, public route: ActivatedRoute,
private toastService: ToastService, private toastService: ToastService,
public modalService: NgbModal, public modalService: NgbModal,
private titleService: Title) { } private titleService: Title) { }
@ViewChild("filterEditor")
private filterEditor: FilterEditorComponent
displayMode = 'smallCards' // largeCards, smallCards, details displayMode = 'smallCards' // largeCards, smallCards, details
get isFiltered() { get isFiltered() {
return this.list.filterRules?.length > 0 return this.list.filterRules?.length > 0
} }
set filterRules(filterRules: FilterRule[]) {
this.filterEditorService.filterRules = filterRules
}
get filterRules(): FilterRule[] {
return this.filterEditorService.filterRules
}
getTitle() { getTitle() {
return this.list.savedViewTitle || "Documents" return this.list.savedViewTitle || "Documents"
} }
@ -66,29 +53,18 @@ export class DocumentListComponent implements OnInit {
this.route.paramMap.subscribe(params => { this.route.paramMap.subscribe(params => {
if (params.has('id')) { if (params.has('id')) {
this.list.savedView = this.savedViewConfigService.getConfig(params.get('id')) this.list.savedView = this.savedViewConfigService.getConfig(params.get('id'))
this.filterEditorService.filterRules = this.list.filterRules
this.titleService.setTitle(`${this.list.savedView.title} - ${environment.appTitle}`) this.titleService.setTitle(`${this.list.savedView.title} - ${environment.appTitle}`)
} else { } else {
this.list.savedView = null this.list.savedView = null
this.filterEditorService.filterRules = this.list.filterRules
this.titleService.setTitle(`Documents - ${environment.appTitle}`) this.titleService.setTitle(`Documents - ${environment.appTitle}`)
} }
this.list.clear() this.list.clear()
this.list.reload() this.list.reload()
}) })
this.filterEditorService.filterRules = this.list.filterRules
} }
applyFilterRules() {
this.list.filterRules = this.filterEditorService.filterRules
}
clearFilterRules() {
this.list.filterRules = this.filterEditorService.filterRules
}
loadViewConfig(config: SavedViewConfig) { loadViewConfig(config: SavedViewConfig) {
this.filterEditorService.filterRules = cloneFilterRules(config.filterRules)
this.list.load(config) this.list.load(config)
} }
@ -113,18 +89,15 @@ export class DocumentListComponent implements OnInit {
} }
clickTag(tagID: number) { clickTag(tagID: number) {
this.filterEditorService.toggleFilterByTag(tagID) this.filterEditor.toggleTag(tagID)
this.applyFilterRules()
} }
clickCorrespondent(correspondentID: number) { clickCorrespondent(correspondentID: number) {
this.filterEditorService.toggleFilterByCorrespondent(correspondentID) this.filterEditor.toggleCorrespondent(correspondentID)
this.applyFilterRules()
} }
clickDocumentType(documentTypeID: number) { clickDocumentType(documentTypeID: number) {
this.filterEditorService.toggleFilterByDocumentType(documentTypeID) this.filterEditor.toggleDocumentType(documentTypeID)
this.applyFilterRules()
} }
} }

View File

@ -1,6 +1,4 @@
import { Component, EventEmitter, Input, Output, ElementRef, ViewChild, OnChanges, SimpleChange } from '@angular/core'; import { Component, EventEmitter, Input, Output, ElementRef, ViewChild, SimpleChange } from '@angular/core';
import { FilterRule } from 'src/app/data/filter-rule';
import { ObjectWithId } from 'src/app/data/object-with-id';
import { NgbDate, NgbDateStruct, NgbDatepicker } from '@ng-bootstrap/ng-bootstrap'; import { NgbDate, NgbDateStruct, NgbDatepicker } from '@ng-bootstrap/ng-bootstrap';
@Component({ @Component({

View File

@ -6,14 +6,14 @@
<input class="form-control form-control-sm" type="text" [(ngModel)]="titleFilter" placeholder="Title"> <input class="form-control form-control-sm" type="text" [(ngModel)]="titleFilter" placeholder="Title">
</div> </div>
<app-filter-dropdown class="col-auto" [(items)]="filterEditorService.tags" [itemsSelected]="filterEditorService.selectedTags" [title]="'Tags'" (toggle)="onToggleTag($event)"></app-filter-dropdown> <app-filter-dropdown class="col-auto" [items]="tags" [itemsSelected]="selectedTags" title="Tags" (toggle)="toggleTag($event.id)"></app-filter-dropdown>
<app-filter-dropdown class="col-auto" [(items)]="filterEditorService.correspondents" [itemsSelected]="filterEditorService.selectedCorrespondents" [title]="'Correspondents'" (toggle)="onToggleCorrespondent($event)"></app-filter-dropdown> <app-filter-dropdown class="col-auto" [items]="correspondents" [itemsSelected]="selectedCorrespondents" title="Correspondents" (toggle)="toggleCorrespondent($event.id)"></app-filter-dropdown>
<app-filter-dropdown class="col-auto" [(items)]="filterEditorService.documentTypes" [itemsSelected]="filterEditorService.selectedDocumentTypes" [title]="'Document Types'" (toggle)="onToggleDocumentType($event)"></app-filter-dropdown> <app-filter-dropdown class="col-auto" [items]="documentTypes" [itemsSelected]="selectedDocumentTypes" title="Document Types" (toggle)="toggleDocumentType($event.id)"></app-filter-dropdown>
<app-filter-dropdown-date class="col-auto" [dateBefore]="filterEditorService.dateCreatedBefore" [dateAfter]="filterEditorService.dateCreatedAfter" [title]="'Created'" (dateBeforeSet)="onDateCreatedBeforeSet($event)" (dateAfterSet)="onDateCreatedAfterSet($event)"></app-filter-dropdown-date> <app-filter-dropdown-date class="col-auto" [dateBefore]="dateCreatedBefore" [dateAfter]="dateCreatedAfter" title="Created" (dateBeforeSet)="onDateCreatedBeforeSet($event)" (dateAfterSet)="onDateCreatedAfterSet($event)"></app-filter-dropdown-date>
<app-filter-dropdown-date class="col-auto" [dateBefore]="filterEditorService.dateAddedBefore" [dateAfter]="filterEditorService.dateAddedAfter" [title]="'Added'" (dateBeforeSet)="onDateAddedBeforeSet($event)" (dateAfterSet)="onDateAddedAfterSet($event)"></app-filter-dropdown-date> <app-filter-dropdown-date class="col-auto" [dateBefore]="dateAddedBefore" [dateAfter]="dateAddedAfter" title="Added" (dateBeforeSet)="onDateAddedBeforeSet($event)" (dateAfterSet)="onDateAddedAfterSet($event)"></app-filter-dropdown-date>
<button class="btn btn-link btn-sm" [disabled]="!filterEditorService.hasFilters()" (click)="clearSelected()"> <button class="btn btn-link btn-sm" [disabled]="!hasFilters()" (click)="clearSelected()">
<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-x" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> <svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-x" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" d="M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708z"/> <path fill-rule="evenodd" d="M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708z"/>
</svg> </svg>

View File

@ -1,11 +1,15 @@
import { Component, EventEmitter, Input, Output, OnInit, OnDestroy } from '@angular/core'; import { Component, EventEmitter, Input, Output, OnInit, OnDestroy } from '@angular/core';
import { FilterEditorViewService } from 'src/app/services/filter-editor-view.service'
import { PaperlessTag } from 'src/app/data/paperless-tag'; import { PaperlessTag } from 'src/app/data/paperless-tag';
import { PaperlessCorrespondent } from 'src/app/data/paperless-correspondent'; import { PaperlessCorrespondent } from 'src/app/data/paperless-correspondent';
import { PaperlessDocumentType } from 'src/app/data/paperless-document-type'; import { PaperlessDocumentType } from 'src/app/data/paperless-document-type';
import { Subject, Subscription } from 'rxjs'; import { Subject, Subscription } from 'rxjs';
import { debounceTime, distinctUntilChanged } from 'rxjs/operators'; import { debounceTime, distinctUntilChanged } from 'rxjs/operators';
import { NgbDateStruct } from '@ng-bootstrap/ng-bootstrap'; import { NgbDateStruct } from '@ng-bootstrap/ng-bootstrap';
import { DocumentTypeService } from 'src/app/services/rest/document-type.service';
import { TagService } from 'src/app/services/rest/tag.service';
import { CorrespondentService } from 'src/app/services/rest/correspondent.service';
import { FilterRule } from 'src/app/data/filter-rule';
import { FILTER_ADDED_AFTER, FILTER_ADDED_BEFORE, FILTER_CORRESPONDENT, FILTER_CREATED_AFTER, FILTER_CREATED_BEFORE, FILTER_DOCUMENT_TYPE, FILTER_HAS_TAG, FILTER_RULE_TYPES, FILTER_TITLE } from 'src/app/data/filter-rule-type';
@Component({ @Component({
selector: 'app-filter-editor', selector: 'app-filter-editor',
@ -14,19 +18,44 @@ import { NgbDateStruct } from '@ng-bootstrap/ng-bootstrap';
}) })
export class FilterEditorComponent implements OnInit, OnDestroy { export class FilterEditorComponent implements OnInit, OnDestroy {
constructor() { } constructor(
private documentTypeService: DocumentTypeService,
private tagService: TagService,
private correspondentService: CorrespondentService
) { }
tags: PaperlessTag[] = []
correspondents: PaperlessCorrespondent[]
documentTypes: PaperlessDocumentType[] = []
@Input() @Input()
filterEditorService: FilterEditorViewService filterRules: FilterRule[]
@Output() @Output()
clear = new EventEmitter() filterRulesChange = new EventEmitter<FilterRule[]>()
hasFilters() {
return this.filterRules.length > 0
}
@Output() get selectedTags(): PaperlessTag[] {
apply = new EventEmitter() let tagRules: FilterRule[] = this.filterRules.filter(fr => fr.type.id == FILTER_HAS_TAG)
return this.tags?.filter(t => tagRules.find(tr => tr.value == t.id))
}
get selectedCorrespondents(): PaperlessCorrespondent[] {
let correspondentRules: FilterRule[] = this.filterRules.filter(fr => fr.type.id == FILTER_CORRESPONDENT)
return this.correspondents?.filter(c => correspondentRules.find(cr => cr.value == c.id))
}
get selectedDocumentTypes(): PaperlessDocumentType[] {
let documentTypeRules: FilterRule[] = this.filterRules.filter(fr => fr.type.id == FILTER_DOCUMENT_TYPE)
return this.documentTypes?.filter(dt => documentTypeRules.find(dtr => dtr.value == dt.id))
}
get titleFilter() { get titleFilter() {
return this.filterEditorService.titleFilter let existingRule = this.filterRules.find(rule => rule.type.id == FILTER_TITLE)
return existingRule ? existingRule.value : ''
} }
set titleFilter(value) { set titleFilter(value) {
@ -37,13 +66,18 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
subscription: Subscription subscription: Subscription
ngOnInit() { ngOnInit() {
this.tagService.listAll().subscribe(result => this.tags = result.results)
this.correspondentService.listAll().subscribe(result => this.correspondents = result.results)
this.documentTypeService.listAll().subscribe(result => this.documentTypes = result.results)
this.titleFilterDebounce = new Subject<string>() this.titleFilterDebounce = new Subject<string>()
this.subscription = this.titleFilterDebounce.pipe( this.subscription = this.titleFilterDebounce.pipe(
debounceTime(400), debounceTime(400),
distinctUntilChanged() distinctUntilChanged()
).subscribe(title => { ).subscribe(title => {
this.filterEditorService.titleFilter = title
this.applyFilters() this.setTitleRule(title)
}) })
} }
@ -54,46 +88,159 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
} }
applyFilters() { applyFilters() {
this.apply.next() this.filterRulesChange.next(this.filterRules)
} }
clearSelected() { clearSelected() {
this.filterEditorService.clear() this.filterRules = []
this.clear.next()
}
onToggleTag(tag: PaperlessTag) {
this.filterEditorService.toggleFilterByTag(tag)
this.applyFilters() this.applyFilters()
} }
onToggleCorrespondent(correspondent: PaperlessCorrespondent) { private toggleFilterRule(filterRuleTypeID: number, value: number) {
this.filterEditorService.toggleFilterByCorrespondent(correspondent)
let filterRuleType = FILTER_RULE_TYPES.find(t => t.id == filterRuleTypeID)
let existingRule = this.filterRules.find(rule => rule.type.id == filterRuleTypeID && rule.value == value)
let existingRuleOfSameType = this.filterRules.find(rule => rule.type.id == filterRuleTypeID)
if (existingRule) {
// if this exact rule already exists, remove it in all cases.
this.filterRules.splice(this.filterRules.indexOf(existingRule), 1)
} else if (filterRuleType.multi || !existingRuleOfSameType) {
// 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})
} else {
// otherwise (i.e., no multi support AND there's already a rule of this type), update the rule.
existingRuleOfSameType.value = value
}
this.applyFilters() this.applyFilters()
} }
onToggleDocumentType(documentType: PaperlessDocumentType) { private setTitleRule(title: string) {
this.filterEditorService.toggleFilterByDocumentType(documentType) let existingRule = this.filterRules.find(rule => rule.type.id == FILTER_TITLE)
if (!existingRule && title) {
this.filterRules.push({type: FILTER_RULE_TYPES.find(t => t.id == FILTER_TITLE), value: title})
} else if (existingRule && !title) {
this.filterRules.splice(this.filterRules.findIndex(rule => rule.type.id == FILTER_TITLE), 1)
} else if (existingRule && title) {
existingRule.value = title
}
this.applyFilters() this.applyFilters()
} }
toggleTag(tagId: number) {
this.toggleFilterRule(FILTER_HAS_TAG, tagId)
}
toggleCorrespondent(correspondentId: number) {
this.toggleFilterRule(FILTER_CORRESPONDENT, correspondentId)
}
toggleDocumentType(documentTypeId: number) {
this.toggleFilterRule(FILTER_DOCUMENT_TYPE, documentTypeId)
}
// Date handling
onDateCreatedBeforeSet(date: NgbDateStruct) { onDateCreatedBeforeSet(date: NgbDateStruct) {
this.filterEditorService.setDateCreatedBefore(date) this.setDateCreatedBefore(date)
this.applyFilters() this.applyFilters()
} }
onDateCreatedAfterSet(date: NgbDateStruct) { onDateCreatedAfterSet(date: NgbDateStruct) {
this.filterEditorService.setDateCreatedAfter(date) this.setDateCreatedAfter(date)
this.applyFilters() this.applyFilters()
} }
onDateAddedBeforeSet(date: NgbDateStruct) { onDateAddedBeforeSet(date: NgbDateStruct) {
this.filterEditorService.setDateAddedBefore(date) this.setDateAddedBefore(date)
this.applyFilters() this.applyFilters()
} }
onDateAddedAfterSet(date: NgbDateStruct) { onDateAddedAfterSet(date: NgbDateStruct) {
this.filterEditorService.setDateAddedAfter(date) this.setDateAddedAfter(date)
this.applyFilters() this.applyFilters()
} }
get dateCreatedBefore(): NgbDateStruct {
let createdBeforeRule: FilterRule = this.filterRules.find(fr => fr.type.id == FILTER_CREATED_BEFORE)
return createdBeforeRule ? {
year: createdBeforeRule.value.substring(0,4),
month: createdBeforeRule.value.substring(5,7),
day: createdBeforeRule.value.substring(8,10)
} : undefined
}
get dateCreatedAfter(): NgbDateStruct {
let createdAfterRule: FilterRule = this.filterRules.find(fr => fr.type.id == FILTER_CREATED_AFTER)
return createdAfterRule ? {
year: createdAfterRule.value.substring(0,4),
month: createdAfterRule.value.substring(5,7),
day: createdAfterRule.value.substring(8,10)
} : undefined
}
get dateAddedBefore(): NgbDateStruct {
let addedBeforeRule: FilterRule = this.filterRules.find(fr => fr.type.id == FILTER_ADDED_BEFORE)
return addedBeforeRule ? {
year: addedBeforeRule.value.substring(0,4),
month: addedBeforeRule.value.substring(5,7),
day: addedBeforeRule.value.substring(8,10)
} : undefined
}
get dateAddedAfter(): NgbDateStruct {
let addedAfterRule: FilterRule = this.filterRules.find(fr => fr.type.id == FILTER_ADDED_AFTER)
return addedAfterRule ? {
year: addedAfterRule.value.substring(0,4),
month: addedAfterRule.value.substring(5,7),
day: addedAfterRule.value.substring(8,10)
} : undefined
}
setDateCreatedBefore(date?: NgbDateStruct) {
if (date) this.setDateFilter(date, FILTER_CREATED_BEFORE)
else this.clearDateFilter(FILTER_CREATED_BEFORE)
}
setDateCreatedAfter(date?: NgbDateStruct) {
if (date) this.setDateFilter(date, FILTER_CREATED_AFTER)
else this.clearDateFilter(FILTER_CREATED_AFTER)
}
setDateAddedBefore(date?: NgbDateStruct) {
if (date) this.setDateFilter(date, FILTER_ADDED_BEFORE)
else this.clearDateFilter(FILTER_ADDED_BEFORE)
}
setDateAddedAfter(date?: NgbDateStruct) {
if (date) this.setDateFilter(date, FILTER_ADDED_AFTER)
else this.clearDateFilter(FILTER_ADDED_AFTER)
}
setDateFilter(date: NgbDateStruct, dateRuleTypeID: number) {
let filterRules = this.filterRules
let existingRule = filterRules.find(rule => rule.type.id == dateRuleTypeID)
let newValue = `${date.year}-${date.month.toString().padStart(2,'0')}-${date.day.toString().padStart(2,'0')}` // YYYY-MM-DD
if (existingRule) {
existingRule.value = newValue
} else {
filterRules.push({type: FILTER_RULE_TYPES.find(t => t.id == dateRuleTypeID), value: newValue})
}
this.filterRules = filterRules
}
clearDateFilter(dateRuleTypeID: number) {
let filterRules = this.filterRules
let existingRule = filterRules.find(rule => rule.type.id == dateRuleTypeID)
filterRules.splice(filterRules.indexOf(existingRule), 1)
this.filterRules = filterRules
}
} }

View File

@ -1,16 +0,0 @@
import { TestBed } from '@angular/core/testing';
import { FilterEditorViewService } from './filter-editor-view.service';
describe('FilterEditorViewService', () => {
let service: FilterEditorViewService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(FilterEditorViewService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

View File

@ -1,182 +0,0 @@
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { TagService } from 'src/app/services/rest/tag.service';
import { CorrespondentService } from 'src/app/services/rest/correspondent.service';
import { DocumentTypeService } from 'src/app/services/rest/document-type.service';
import { ObjectWithId } from 'src/app/data/object-with-id';
import { FilterRule } from 'src/app/data/filter-rule';
import { FilterRuleType, FILTER_RULE_TYPES, FILTER_CORRESPONDENT, FILTER_DOCUMENT_TYPE, FILTER_HAS_TAG, FILTER_TITLE, FILTER_ADDED_BEFORE, FILTER_ADDED_AFTER, FILTER_CREATED_BEFORE, FILTER_CREATED_AFTER, FILTER_CREATED_YEAR, FILTER_CREATED_MONTH, FILTER_CREATED_DAY } from 'src/app/data/filter-rule-type';
import { Results } from 'src/app/data/results'
import { PaperlessTag } from 'src/app/data/paperless-tag';
import { PaperlessCorrespondent } from 'src/app/data/paperless-correspondent';
import { PaperlessDocumentType } from 'src/app/data/paperless-document-type';
import { NgbDate, NgbDateStruct } from '@ng-bootstrap/ng-bootstrap';
@Injectable({
providedIn: 'root'
})
export class FilterEditorViewService {
tags: PaperlessTag[] = []
correspondents: PaperlessCorrespondent[]
documentTypes: PaperlessDocumentType[] = []
filterRules: FilterRule[] = []
constructor(private tagService: TagService, private documentTypeService: DocumentTypeService, private correspondentService: CorrespondentService) {
this.tagService.listAll().subscribe(result => this.tags = result.results)
this.correspondentService.listAll().subscribe(result => this.correspondents = result.results)
this.documentTypeService.listAll().subscribe(result => this.documentTypes = result.results)
}
clear() {
this.filterRules = []
}
hasFilters() {
return this.filterRules.length > 0
}
set titleFilter(title: string) {
let existingRule = this.filterRules.find(rule => rule.type.id == FILTER_TITLE)
if (!existingRule && title) {
this.filterRules.push({type: FILTER_RULE_TYPES.find(t => t.id == FILTER_TITLE), value: title})
} else if (existingRule && !title) {
this.filterRules.splice(this.filterRules.findIndex(rule => rule.type.id == FILTER_TITLE), 1)
} else if (existingRule && title) {
existingRule.value = title
}
}
get titleFilter(): string {
let existingRule = this.filterRules.find(rule => rule.type.id == FILTER_TITLE)
return existingRule ? existingRule.value : ''
}
get selectedTags(): PaperlessTag[] {
let tagRules: FilterRule[] = this.filterRules.filter(fr => fr.type.id == FILTER_HAS_TAG)
return this.tags?.filter(t => tagRules.find(tr => tr.value == t.id))
}
get selectedCorrespondents(): PaperlessCorrespondent[] {
let correspondentRules: FilterRule[] = this.filterRules.filter(fr => fr.type.id == FILTER_CORRESPONDENT)
return this.correspondents?.filter(c => correspondentRules.find(cr => cr.value == c.id))
}
get selectedDocumentTypes(): PaperlessDocumentType[] {
let documentTypeRules: FilterRule[] = this.filterRules.filter(fr => fr.type.id == FILTER_DOCUMENT_TYPE)
return this.documentTypes?.filter(dt => documentTypeRules.find(dtr => dtr.value == dt.id))
}
toggleFilterByTag(tag: PaperlessTag | number) {
if (typeof tag == 'number') tag = this.tags?.find(t => t.id == tag)
this.toggleFilterByItem(tag, FILTER_HAS_TAG)
}
toggleFilterByCorrespondent(correspondent: PaperlessCorrespondent | number) {
if (typeof correspondent == 'number') correspondent = this.correspondents?.find(t => t.id == correspondent)
this.toggleFilterByItem(correspondent, FILTER_CORRESPONDENT)
}
toggleFilterByDocumentType(documentType: PaperlessDocumentType | number) {
if (typeof documentType == 'number') documentType = this.documentTypes?.find(t => t.id == documentType)
this.toggleFilterByItem(documentType, FILTER_DOCUMENT_TYPE)
}
private toggleFilterByItem(item: ObjectWithId, filterRuleTypeID: number) {
let filterRules = this.filterRules
let filterRuleType: FilterRuleType = FILTER_RULE_TYPES.find(t => t.id == filterRuleTypeID)
let existingRules = filterRules.filter(rule => rule.type.id == filterRuleType.id)
let existingItemRule = existingRules?.find(rule => rule.value == item.id)
if (existingRules && existingItemRule) { // if exact rule exists just remove
filterRules.splice(filterRules.indexOf(existingItemRule), 1)
} else if (existingRules.length > 0 && filterRuleType.multi) { // e.g. tags can have multiple
filterRules.push({type: filterRuleType, value: item.id})
} else if (existingRules.length > 0) { // correspondents & documentTypes can only be one
filterRules.find(rule => rule.type.id == filterRuleType.id).value = item.id
} else {
filterRules.push({type: filterRuleType, value: item.id})
}
this.filterRules = filterRules
}
get dateCreatedBefore(): NgbDateStruct {
let createdBeforeRule: FilterRule = this.filterRules.find(fr => fr.type.id == FILTER_CREATED_BEFORE)
return createdBeforeRule ? {
year: createdBeforeRule.value.substring(0,4),
month: createdBeforeRule.value.substring(5,7),
day: createdBeforeRule.value.substring(8,10)
} : undefined
}
get dateCreatedAfter(): NgbDateStruct {
let createdAfterRule: FilterRule = this.filterRules.find(fr => fr.type.id == FILTER_CREATED_AFTER)
return createdAfterRule ? {
year: createdAfterRule.value.substring(0,4),
month: createdAfterRule.value.substring(5,7),
day: createdAfterRule.value.substring(8,10)
} : undefined
}
get dateAddedBefore(): NgbDateStruct {
let addedBeforeRule: FilterRule = this.filterRules.find(fr => fr.type.id == FILTER_ADDED_BEFORE)
return addedBeforeRule ? {
year: addedBeforeRule.value.substring(0,4),
month: addedBeforeRule.value.substring(5,7),
day: addedBeforeRule.value.substring(8,10)
} : undefined
}
get dateAddedAfter(): NgbDateStruct {
let addedAfterRule: FilterRule = this.filterRules.find(fr => fr.type.id == FILTER_ADDED_AFTER)
return addedAfterRule ? {
year: addedAfterRule.value.substring(0,4),
month: addedAfterRule.value.substring(5,7),
day: addedAfterRule.value.substring(8,10)
} : undefined
}
setDateCreatedBefore(date?: NgbDateStruct) {
if (date) this.setDateFilter(date, FILTER_CREATED_BEFORE)
else this.clearDateFilter(FILTER_CREATED_BEFORE)
}
setDateCreatedAfter(date?: NgbDateStruct) {
if (date) this.setDateFilter(date, FILTER_CREATED_AFTER)
else this.clearDateFilter(FILTER_CREATED_AFTER)
}
setDateAddedBefore(date?: NgbDateStruct) {
if (date) this.setDateFilter(date, FILTER_ADDED_BEFORE)
else this.clearDateFilter(FILTER_ADDED_BEFORE)
}
setDateAddedAfter(date?: NgbDateStruct) {
if (date) this.setDateFilter(date, FILTER_ADDED_AFTER)
else this.clearDateFilter(FILTER_ADDED_AFTER)
}
setDateFilter(date: NgbDateStruct, dateRuleTypeID: number) {
let filterRules = this.filterRules
let existingRule = filterRules.find(rule => rule.type.id == dateRuleTypeID)
let newValue = `${date.year}-${date.month.toString().padStart(2,'0')}-${date.day.toString().padStart(2,'0')}` // YYYY-MM-DD
if (existingRule) {
existingRule.value = newValue
} else {
filterRules.push({type: FILTER_RULE_TYPES.find(t => t.id == dateRuleTypeID), value: newValue})
}
this.filterRules = filterRules
}
clearDateFilter(dateRuleTypeID: number) {
let filterRules = this.filterRules
let existingRule = filterRules.find(rule => rule.type.id == dateRuleTypeID)
filterRules.splice(filterRules.indexOf(existingRule), 1)
this.filterRules = filterRules
}
}