mirror of
				https://github.com/paperless-ngx/paperless-ngx.git
				synced 2025-10-30 03:56:23 -05:00 
			
		
		
		
	Merge branch 'dev' into feature-server-side-saved-views
This commit is contained in:
		| @@ -63,7 +63,7 @@ | ||||
| </app-page-header> | ||||
|  | ||||
| <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 class="d-flex justify-content-between align-items-center"> | ||||
|   | ||||
| @@ -2,21 +2,14 @@ import { Component, OnInit, ViewChild } from '@angular/core'; | ||||
| import { Title } from '@angular/platform-browser'; | ||||
| import { ActivatedRoute } from '@angular/router'; | ||||
| 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 { 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 { SavedViewConfigService } from 'src/app/services/saved-view-config.service'; | ||||
| import { Toast, ToastService } from 'src/app/services/toast.service'; | ||||
| 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 { 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({ | ||||
|   selector: 'app-document-list', | ||||
|   templateUrl: './document-list.component.html', | ||||
| @@ -27,26 +20,20 @@ export class DocumentListComponent implements OnInit { | ||||
|   constructor( | ||||
|     public list: DocumentListViewService, | ||||
|     public savedViewConfigService: SavedViewConfigService, | ||||
|     public filterEditorService: FilterEditorViewService, | ||||
|     public route: ActivatedRoute, | ||||
|     private toastService: ToastService, | ||||
|     public modalService: NgbModal, | ||||
|     private titleService: Title) { } | ||||
|  | ||||
|   @ViewChild("filterEditor") | ||||
|   private filterEditor: FilterEditorComponent | ||||
|  | ||||
|   displayMode = 'smallCards' // largeCards, smallCards, details | ||||
|  | ||||
|   get isFiltered() { | ||||
|     return this.list.filterRules?.length > 0 | ||||
|   } | ||||
|  | ||||
|   set filterRules(filterRules: FilterRule[]) { | ||||
|     this.filterEditorService.filterRules = filterRules | ||||
|   } | ||||
|  | ||||
|   get filterRules(): FilterRule[] { | ||||
|     return this.filterEditorService.filterRules | ||||
|   } | ||||
|  | ||||
|   getTitle() { | ||||
|     return this.list.savedViewTitle || "Documents" | ||||
|   } | ||||
| @@ -66,29 +53,18 @@ export class DocumentListComponent implements OnInit { | ||||
|     this.route.paramMap.subscribe(params => { | ||||
|       if (params.has('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}`) | ||||
|       } else { | ||||
|         this.list.savedView = null | ||||
|         this.filterEditorService.filterRules = this.list.filterRules | ||||
|         this.titleService.setTitle(`Documents - ${environment.appTitle}`) | ||||
|       } | ||||
|       this.list.clear() | ||||
|       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) { | ||||
|     this.filterEditorService.filterRules = cloneFilterRules(config.filterRules) | ||||
|     this.list.load(config) | ||||
|   } | ||||
|  | ||||
| @@ -113,18 +89,15 @@ export class DocumentListComponent implements OnInit { | ||||
|   } | ||||
|  | ||||
|   clickTag(tagID: number) { | ||||
|     this.filterEditorService.toggleFilterByTag(tagID) | ||||
|     this.applyFilterRules() | ||||
|     this.filterEditor.toggleTag(tagID) | ||||
|   } | ||||
|  | ||||
|   clickCorrespondent(correspondentID: number) { | ||||
|     this.filterEditorService.toggleFilterByCorrespondent(correspondentID) | ||||
|     this.applyFilterRules() | ||||
|     this.filterEditor.toggleCorrespondent(correspondentID) | ||||
|   } | ||||
|  | ||||
|   clickDocumentType(documentTypeID: number) { | ||||
|     this.filterEditorService.toggleFilterByDocumentType(documentTypeID) | ||||
|     this.applyFilterRules() | ||||
|     this.filterEditor.toggleDocumentType(documentTypeID) | ||||
|   } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -1,6 +1,4 @@ | ||||
| import { Component, EventEmitter, Input, Output, ElementRef, ViewChild, OnChanges, SimpleChange } from '@angular/core'; | ||||
| import { FilterRule } from 'src/app/data/filter-rule'; | ||||
| import { ObjectWithId } from 'src/app/data/object-with-id'; | ||||
| import { Component, EventEmitter, Input, Output, ElementRef, ViewChild, SimpleChange } from '@angular/core'; | ||||
| import { NgbDate, NgbDateStruct, NgbDatepicker } from '@ng-bootstrap/ng-bootstrap'; | ||||
|  | ||||
| @Component({ | ||||
|   | ||||
| @@ -6,14 +6,14 @@ | ||||
|     <input class="form-control form-control-sm" type="text" [(ngModel)]="titleFilter" placeholder="Title"> | ||||
|   </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)]="filterEditorService.correspondents" [itemsSelected]="filterEditorService.selectedCorrespondents" [title]="'Correspondents'" (toggle)="onToggleCorrespondent($event)"></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]="tags" [itemsSelected]="selectedTags" title="Tags" (toggle)="toggleTag($event.id)"></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]="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]="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]="dateCreatedBefore" [dateAfter]="dateCreatedAfter" title="Created" (dateBeforeSet)="onDateCreatedBeforeSet($event)" (dateAfterSet)="onDateCreatedAfterSet($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"> | ||||
|       <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> | ||||
|   | ||||
| @@ -1,11 +1,15 @@ | ||||
| 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 { PaperlessCorrespondent } from 'src/app/data/paperless-correspondent'; | ||||
| import { PaperlessDocumentType } from 'src/app/data/paperless-document-type'; | ||||
| import { Subject, Subscription } from 'rxjs'; | ||||
| import { debounceTime, distinctUntilChanged } from 'rxjs/operators'; | ||||
| 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({ | ||||
|   selector: 'app-filter-editor', | ||||
| @@ -14,19 +18,44 @@ import { NgbDateStruct } from '@ng-bootstrap/ng-bootstrap'; | ||||
| }) | ||||
| export class FilterEditorComponent implements OnInit, OnDestroy { | ||||
|  | ||||
|   constructor() { } | ||||
|   constructor( | ||||
|     private documentTypeService: DocumentTypeService, | ||||
|     private tagService: TagService, | ||||
|     private correspondentService: CorrespondentService | ||||
|   ) { } | ||||
|  | ||||
|   tags: PaperlessTag[] = [] | ||||
|   correspondents: PaperlessCorrespondent[] | ||||
|   documentTypes: PaperlessDocumentType[] = [] | ||||
|  | ||||
|   @Input() | ||||
|   filterEditorService: FilterEditorViewService | ||||
|   filterRules: FilterRule[] | ||||
|  | ||||
|   @Output() | ||||
|   clear = new EventEmitter() | ||||
|   filterRulesChange = new EventEmitter<FilterRule[]>() | ||||
|    | ||||
|   hasFilters() { | ||||
|     return this.filterRules.length > 0 | ||||
|   } | ||||
|  | ||||
|   @Output() | ||||
|   apply = new EventEmitter() | ||||
|   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)) | ||||
|   } | ||||
|  | ||||
|   get titleFilter() { | ||||
|     return this.filterEditorService.titleFilter | ||||
|     let existingRule = this.filterRules.find(rule => rule.type.id == FILTER_TITLE) | ||||
|     return existingRule ? existingRule.value : '' | ||||
|   } | ||||
|  | ||||
|   set titleFilter(value) { | ||||
| @@ -37,13 +66,18 @@ export class FilterEditorComponent implements OnInit, OnDestroy { | ||||
|   subscription: Subscription | ||||
|  | ||||
|   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.subscription = this.titleFilterDebounce.pipe( | ||||
|       debounceTime(400), | ||||
|       distinctUntilChanged() | ||||
|     ).subscribe(title => { | ||||
|       this.filterEditorService.titleFilter = title | ||||
|       this.applyFilters() | ||||
|        | ||||
|       this.setTitleRule(title) | ||||
|     }) | ||||
|   } | ||||
|  | ||||
| @@ -54,46 +88,159 @@ export class FilterEditorComponent implements OnInit, OnDestroy { | ||||
|   } | ||||
|  | ||||
|   applyFilters() { | ||||
|     this.apply.next() | ||||
|     this.filterRulesChange.next(this.filterRules) | ||||
|   } | ||||
|  | ||||
|   clearSelected() { | ||||
|     this.filterEditorService.clear() | ||||
|     this.clear.next() | ||||
|   } | ||||
|  | ||||
|   onToggleTag(tag: PaperlessTag) { | ||||
|     this.filterEditorService.toggleFilterByTag(tag) | ||||
|     this.filterRules = [] | ||||
|     this.applyFilters() | ||||
|   } | ||||
|  | ||||
|   onToggleCorrespondent(correspondent: PaperlessCorrespondent) { | ||||
|     this.filterEditorService.toggleFilterByCorrespondent(correspondent) | ||||
|   private toggleFilterRule(filterRuleTypeID: number, value: number) { | ||||
|  | ||||
|     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() | ||||
|   } | ||||
|  | ||||
|   onToggleDocumentType(documentType: PaperlessDocumentType) { | ||||
|     this.filterEditorService.toggleFilterByDocumentType(documentType) | ||||
|   private setTitleRule(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 | ||||
|     } | ||||
|     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) { | ||||
|     this.filterEditorService.setDateCreatedBefore(date) | ||||
|     this.setDateCreatedBefore(date) | ||||
|     this.applyFilters() | ||||
|   } | ||||
|  | ||||
|   onDateCreatedAfterSet(date: NgbDateStruct) { | ||||
|     this.filterEditorService.setDateCreatedAfter(date) | ||||
|     this.setDateCreatedAfter(date) | ||||
|     this.applyFilters() | ||||
|   } | ||||
|  | ||||
|   onDateAddedBeforeSet(date: NgbDateStruct) { | ||||
|     this.filterEditorService.setDateAddedBefore(date) | ||||
|     this.setDateAddedBefore(date) | ||||
|     this.applyFilters() | ||||
|   } | ||||
|  | ||||
|   onDateAddedAfterSet(date: NgbDateStruct) { | ||||
|     this.filterEditorService.setDateAddedAfter(date) | ||||
|     this.setDateAddedAfter(date) | ||||
|     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 | ||||
|   } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -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(); | ||||
|   }); | ||||
| }); | ||||
| @@ -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 | ||||
|   } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 jonaswinkler
					jonaswinkler