mirror of
				https://github.com/paperless-ngx/paperless-ngx.git
				synced 2025-10-30 03:56:23 -05:00 
			
		
		
		
	Completely refactored because programming
Extracted filter editor to service Made all components actually reactive
This commit is contained in:
		| @@ -29,6 +29,7 @@ import { AppFrameComponent } from './components/app-frame/app-frame.component'; | ||||
| import { ToastsComponent } from './components/common/toasts/toasts.component'; | ||||
| import { FilterEditorComponent } from './components/filter-editor/filter-editor.component'; | ||||
| import { FilterDropdownComponent } from './components/filter-editor/filter-dropdown/filter-dropdown.component'; | ||||
| import { FilterDropdownButtonComponent } from './components/filter-editor/filter-dropdown/filter-dropdown-button/filter-dropdown-button.component'; | ||||
| import { FilterDropdownDateComponent } from './components/filter-editor/filter-dropdown-date/filter-dropdown-date.component'; | ||||
| import { DocumentCardLargeComponent } from './components/document-list/document-card-large/document-card-large.component'; | ||||
| import { DocumentCardSmallComponent } from './components/document-list/document-card-small/document-card-small.component'; | ||||
| @@ -77,6 +78,7 @@ import { FilterPipe } from './pipes/filter.pipe'; | ||||
|     ToastsComponent, | ||||
|     FilterEditorComponent, | ||||
|     FilterDropdownComponent, | ||||
|     FilterDropdownButtonComponent, | ||||
|     FilterDropdownDateComponent, | ||||
|     DocumentCardLargeComponent, | ||||
|     DocumentCardSmallComponent, | ||||
|   | ||||
| @@ -64,7 +64,7 @@ | ||||
|  | ||||
| <div class="card w-100 mb-3"> | ||||
|   <div class="card-body"> | ||||
|     <app-filter-editor [(filterRules)]="filterRules" (apply)="applyFilterRules()" (clear)="clearFilterRules()" #filterEditor></app-filter-editor> | ||||
|     <app-filter-editor [(filterEditorService)]="filterEditorService" (apply)="applyFilterRules()" (clear)="clearFilterRules()" #filterEditor></app-filter-editor> | ||||
|   </div> | ||||
| </div> | ||||
|  | ||||
|   | ||||
| @@ -6,6 +6,7 @@ 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'; | ||||
| @@ -26,6 +27,7 @@ export class DocumentListComponent implements OnInit { | ||||
|   constructor( | ||||
|     public list: DocumentListViewService, | ||||
|     public savedViewConfigService: SavedViewConfigService, | ||||
|     public filterEditorService: FilterEditorViewService, | ||||
|     public route: ActivatedRoute, | ||||
|     private toastService: ToastService, | ||||
|     public modalService: NgbModal, | ||||
| @@ -33,14 +35,18 @@ export class DocumentListComponent implements OnInit { | ||||
|  | ||||
|   displayMode = 'smallCards' // largeCards, smallCards, details | ||||
|  | ||||
|   filterRules: FilterRule[] = [] | ||||
|  | ||||
|   @ViewChild('filterEditor') filterEditor: FilterEditorComponent | ||||
|  | ||||
|   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" | ||||
|   } | ||||
| @@ -60,28 +66,29 @@ export class DocumentListComponent implements OnInit { | ||||
|     this.route.paramMap.subscribe(params => { | ||||
|       if (params.has('id')) { | ||||
|         this.list.savedView = this.savedViewConfigService.getConfig(params.get('id')) | ||||
|         this.filterRules = this.list.filterRules | ||||
|         this.filterEditorService.filterRules = this.list.filterRules | ||||
|         this.titleService.setTitle(`${this.list.savedView.title} - ${environment.appTitle}`) | ||||
|       } else { | ||||
|         this.list.savedView = null | ||||
|         this.filterRules = this.list.filterRules | ||||
|         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.filterRules | ||||
|     this.list.filterRules = this.filterEditorService.filterRules | ||||
|   } | ||||
|  | ||||
|   clearFilterRules() { | ||||
|     this.list.filterRules = this.filterRules | ||||
|     this.list.filterRules = this.filterEditorService.filterRules | ||||
|   } | ||||
|  | ||||
|   loadViewConfig(config: SavedViewConfig) { | ||||
|     this.filterRules = cloneFilterRules(config.filterRules) | ||||
|     this.filterEditorService.filterRules = cloneFilterRules(config.filterRules) | ||||
|     this.list.load(config) | ||||
|   } | ||||
|  | ||||
| @@ -106,15 +113,18 @@ export class DocumentListComponent implements OnInit { | ||||
|   } | ||||
|  | ||||
|   clickTag(tagID: number) { | ||||
|     this.filterEditor.toggleFilterByItem(tagID, FILTER_HAS_TAG) | ||||
|     this.filterEditorService.toggleFitlerByTagID(tagID) | ||||
|     this.applyFilterRules() | ||||
|   } | ||||
|  | ||||
|   clickCorrespondent(correspondentID: number) { | ||||
|     this.filterEditor.toggleFilterByItem(correspondentID, FILTER_CORRESPONDENT) | ||||
|     this.filterEditorService.toggleFitlerByCorrespondentID(correspondentID) | ||||
|     this.applyFilterRules() | ||||
|   } | ||||
|  | ||||
|   clickDocumentType(documentTypeID: number) { | ||||
|     this.filterEditor.toggleFilterByItem(documentTypeID, FILTER_DOCUMENT_TYPE) | ||||
|     this.filterEditorService.toggleFitlerByDocumentTypeID(documentTypeID) | ||||
|     this.applyFilterRules() | ||||
|   } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -11,7 +11,7 @@ | ||||
|         <div class="list-group-item d-flex flex-column align-items-start" role="menuitem"> | ||||
|           <div class="mb-1"><small>Before</small></div> | ||||
|           <div class="input-group input-group-sm"> | ||||
|             <input class="form-control" type="text" placeholder="yyyy-mm-dd" name="before" [(ngModel)]="dateBefore" ngbDatepicker (dateSelect)="dateSelected($event)" #dpBefore="ngbDatepicker"> | ||||
|             <input class="form-control" type="text" placeholder="yyyy-mm-dd" name="before" [(ngModel)]="_dateBefore" ngbDatepicker (dateSelect)="onDateSelected($event)" #dpBefore="ngbDatepicker"> | ||||
|             <div class="input-group-append"> | ||||
|               <button class="btn btn-outline-secondary btn-sm" (click)="dpBefore.toggle()" type="button"> | ||||
|                 <svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-calendar-date" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> | ||||
| @@ -25,7 +25,7 @@ | ||||
|         <div class="list-group-item d-flex flex-column align-items-start" role="menuitem"> | ||||
|           <div class="mb-1"><small>After</small></div> | ||||
|           <div class="input-group"> | ||||
|             <input class="form-control form-control-sm" type="text" placeholder="yyyy-mm-dd" name="after" [(ngModel)]="dateAfter" ngbDatepicker (dateSelect)="dateSelected($event)" #dpAfter="ngbDatepicker"> | ||||
|             <input class="form-control form-control-sm" type="text" placeholder="yyyy-mm-dd" name="after" [(ngModel)]="_dateAfter" ngbDatepicker (dateSelect)="onDateSelected($event)" #dpAfter="ngbDatepicker"> | ||||
|             <div class="input-group-append"> | ||||
|               <button class="btn btn-outline-secondary btn-sm" (click)="dpAfter.toggle()" type="button"> | ||||
|                 <svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-calendar-date" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> | ||||
|   | ||||
| @@ -1,6 +1,5 @@ | ||||
| import { Component, EventEmitter, Input, OnInit, Output, ElementRef, ViewChild } from '@angular/core'; | ||||
| import { Component, EventEmitter, Input, Output, ElementRef, ViewChild } from '@angular/core'; | ||||
| import { FilterRule } from 'src/app/data/filter-rule'; | ||||
| import { FilterRuleType, FILTER_RULE_TYPES } from 'src/app/data/filter-rule-type'; | ||||
| import { ObjectWithId } from 'src/app/data/object-with-id'; | ||||
| import { NgbDate, NgbDateStruct } from '@ng-bootstrap/ng-bootstrap'; | ||||
|  | ||||
| @@ -12,23 +11,25 @@ import { NgbDate, NgbDateStruct } from '@ng-bootstrap/ng-bootstrap'; | ||||
| export class FilterDropdownDateComponent { | ||||
|  | ||||
|   @Input() | ||||
|   filterRuleTypeIDs: number[] = [] | ||||
|  | ||||
|   @Output() | ||||
|   selected = new EventEmitter() | ||||
|  | ||||
|   filterRuleTypes: FilterRuleType[] = [] | ||||
|   title: string | ||||
|   dateAfter: NgbDateStruct | ||||
|   dateBefore: NgbDateStruct | ||||
|  | ||||
|   ngOnInit(): void { | ||||
|     this.filterRuleTypes = this.filterRuleTypeIDs.map(id => FILTER_RULE_TYPES.find(rt => rt.id == id)) | ||||
|     this.title = this.filterRuleTypes[0].displayName | ||||
|   } | ||||
|   @Input() | ||||
|   dateAfter: NgbDateStruct | ||||
|  | ||||
|   @Input() | ||||
|   title: string | ||||
|  | ||||
|   @Output() | ||||
|   dateBeforeSet = new EventEmitter() | ||||
|  | ||||
|   @Output() | ||||
|   dateAfterSet = new EventEmitter() | ||||
|  | ||||
|   _dateBefore: NgbDateStruct | ||||
|   _dateAfter: NgbDateStruct | ||||
|  | ||||
|   setDateQuickFilter(range: any) { | ||||
|     this.dateAfter = this.dateBefore = undefined | ||||
|     this._dateAfter = this._dateBefore = undefined | ||||
|     let date = new Date() | ||||
|     let newDate: NgbDateStruct = { year: date.getFullYear(), month: date.getMonth() + 1, day: date.getDate() } | ||||
|     switch (typeof range) { | ||||
| @@ -47,17 +48,12 @@ export class FilterDropdownDateComponent { | ||||
|       default: | ||||
|         break | ||||
|     } | ||||
|     this.dateAfter = newDate | ||||
|     this.dateSelected(this.dateAfter) | ||||
|     this._dateAfter = newDate | ||||
|     this.onDateSelected(this._dateAfter) | ||||
|   } | ||||
|  | ||||
|   dateSelected(date:NgbDateStruct) { | ||||
|     let isAfter = NgbDate.from(this.dateAfter).equals(date) | ||||
|  | ||||
|     let filterRuleType = this.filterRuleTypes.find(rt => rt.filtervar.indexOf(isAfter ? 'gt' : 'lt') > -1) | ||||
|     if (filterRuleType) { | ||||
|       let dateFilterRule:FilterRule = {value: `${date.year}-${date.month.toString().padStart(2,'0')}-${date.day.toString().padStart(2,'0')}`, type: filterRuleType} | ||||
|       this.selected.emit(dateFilterRule) | ||||
|     } | ||||
|   onDateSelected(date:NgbDateStruct) { | ||||
|     let emitter = this._dateAfter && NgbDate.from(this._dateAfter).equals(date) ? this.dateAfterSet : this.dateBeforeSet | ||||
|     emitter.emit(date) | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,12 @@ | ||||
| <button class="list-group-item list-group-item-action d-flex align-items-center" role="menuitem" (click)="toggleItem()"> | ||||
|   <div class="selected-icon mr-1"> | ||||
|     <svg *ngIf="selected" width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-check" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> | ||||
|       <path fill-rule="evenodd" d="M10.97 4.97a.75.75 0 0 1 1.071 1.05l-3.992 4.99a.75.75 0 0 1-1.08.02L4.324 8.384a.75.75 0 1 1 1.06-1.06l2.094 2.093 3.473-4.425a.236.236 0 0 1 .02-.022z"/> | ||||
|     </svg> | ||||
|   </div> | ||||
|   <div class="mr-1"> | ||||
|     <app-tag *ngIf="display == 'tag'; else displayName" [tag]="item" [clickable]="true" linkTitle="Filter by tag"></app-tag> | ||||
|     <ng-template #displayName><small>{{item.name}}</small></ng-template> | ||||
|   </div> | ||||
|   <div class="badge bg-primary text-light rounded-pill ml-auto">{{item.document_count}}</div> | ||||
| </button> | ||||
| @@ -0,0 +1,4 @@ | ||||
| .selected-icon { | ||||
|   min-width: 1em; | ||||
|   min-height: 1em; | ||||
| } | ||||
| @@ -0,0 +1,25 @@ | ||||
| import { ComponentFixture, TestBed } from '@angular/core/testing'; | ||||
|  | ||||
| import { FilterDropodownButtonComponent } from './filter-dropdown-button.component'; | ||||
|  | ||||
| describe('FilterDropodownButtonComponent', () => { | ||||
|   let component: FilterDropodownButtonComponent; | ||||
|   let fixture: ComponentFixture<FilterDropodownButtonComponent>; | ||||
|  | ||||
|   beforeEach(async () => { | ||||
|     await TestBed.configureTestingModule({ | ||||
|       declarations: [ FilterDropodownButtonComponent ] | ||||
|     }) | ||||
|     .compileComponents(); | ||||
|   }); | ||||
|  | ||||
|   beforeEach(() => { | ||||
|     fixture = TestBed.createComponent(FilterDropodownButtonComponent); | ||||
|     component = fixture.componentInstance; | ||||
|     fixture.detectChanges(); | ||||
|   }); | ||||
|  | ||||
|   it('should create', () => { | ||||
|     expect(component).toBeTruthy(); | ||||
|   }); | ||||
| }); | ||||
| @@ -0,0 +1,32 @@ | ||||
| import { Component, EventEmitter, Input, Output } from '@angular/core'; | ||||
| 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-filter-dropdown-button', | ||||
|   templateUrl: './filter-dropdown-button.component.html', | ||||
|   styleUrls: ['./filter-dropdown-button.component.scss'] | ||||
| }) | ||||
| export class FilterDropdownButtonComponent { | ||||
|  | ||||
|   constructor() { } | ||||
|  | ||||
|   @Input() | ||||
|   item: PaperlessTag | PaperlessDocumentType | PaperlessCorrespondent | ||||
|  | ||||
|   @Input() | ||||
|   display: string | ||||
|  | ||||
|   @Input() | ||||
|   selected: boolean | ||||
|  | ||||
|   @Output() | ||||
|   toggle = new EventEmitter() | ||||
|  | ||||
|  | ||||
|   toggleItem(): void { | ||||
|     this.selected = !this.selected | ||||
|     this.toggle.emit(this.item) | ||||
|   } | ||||
| } | ||||
| @@ -3,19 +3,10 @@ | ||||
|   <div class="dropdown-menu quick-filter shadow" ngbDropdownMenu attr.aria-labelledby="dropdown{{title}}"> | ||||
|     <div class="list-group list-group-flush"> | ||||
|       <input class="list-group-item form-control form-control-sm" type="text" [(ngModel)]="filterText" placeholder="Filter {{title}}" (keyup.enter)="listFilterEnter()" #listFilterTextInput> | ||||
|       <ng-container *ngIf="(items | filter: filterText).length > 0"> | ||||
|         <button class="list-group-item list-group-item-action d-flex align-items-center" role="menuitem" *ngFor="let item of items | filter: filterText; let i = index" (click)="toggleItem(item)"> | ||||
|           <div class="selected-icon mr-1"> | ||||
|             <svg *ngIf="itemsActive.includes(item)" width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-check" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> | ||||
|               <path fill-rule="evenodd" d="M10.97 4.97a.75.75 0 0 1 1.071 1.05l-3.992 4.99a.75.75 0 0 1-1.08.02L4.324 8.384a.75.75 0 1 1 1.06-1.06l2.094 2.093 3.473-4.425a.236.236 0 0 1 .02-.022z"/> | ||||
|             </svg> | ||||
|           </div> | ||||
|           <div class="mr-1"> | ||||
|             <app-tag *ngIf="display == 'tag'; else displayName" [tag]="item" [clickable]="true" linkTitle="Filter by tag"></app-tag> | ||||
|             <ng-template #displayName><small>{{item.name}}</small></ng-template> | ||||
|           </div> | ||||
|           <div class="badge bg-primary text-light rounded-pill ml-auto">{{item.document_count}}</div> | ||||
|         </button> | ||||
|       <ng-container *ngIf="(items$ | async)?.results as items"> | ||||
|         <ng-container *ngFor="let item of items | filter: filterText; let i = index"> | ||||
|           <app-filter-dropdown-button [item]="item" [display]="display" [selected]="isItemSelected(item)" (toggle)="toggleItem($event)"></app-filter-dropdown-button> | ||||
|         </ng-container> | ||||
|       </ng-container> | ||||
|     </div> | ||||
|   </div> | ||||
|   | ||||
| @@ -2,9 +2,4 @@ | ||||
|   min-width: 250px; | ||||
|   max-height: 400px; | ||||
|   overflow-y: scroll; | ||||
|  | ||||
|   .selected-icon { | ||||
|     min-width: 1em; | ||||
|     min-height: 1em; | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| import { Component, EventEmitter, Input, OnInit, Output, ElementRef, ViewChild } from '@angular/core'; | ||||
| import { FilterRuleType, FILTER_RULE_TYPES } from 'src/app/data/filter-rule-type'; | ||||
| import { Observable } from 'rxjs'; | ||||
| import { Results } from 'src/app/data/results'; | ||||
| import { ObjectWithId } from 'src/app/data/object-with-id'; | ||||
| import { FilterPipe } from  'src/app/pipes/filter.pipe'; | ||||
|  | ||||
| @@ -13,29 +14,37 @@ export class FilterDropdownComponent implements OnInit { | ||||
|   constructor(private filterPipe: FilterPipe) { } | ||||
|  | ||||
|   @Input() | ||||
|   filterRuleTypeID: number | ||||
|   items$: Observable<Results<ObjectWithId>> | ||||
|  | ||||
|   @Input() | ||||
|   itemsSelected: ObjectWithId[] | ||||
|  | ||||
|   @Input() | ||||
|   title: string | ||||
|  | ||||
|   @Input() | ||||
|   display: string | ||||
|  | ||||
|   @Output() | ||||
|   toggle = new EventEmitter() | ||||
|  | ||||
|   @ViewChild('listFilterTextInput') listFilterTextInput: ElementRef | ||||
|  | ||||
|   items: ObjectWithId[] = [] | ||||
|   itemsActive: ObjectWithId[] = [] | ||||
|   title: string | ||||
|   filterText: string | ||||
|   display: string | ||||
|   items: ObjectWithId[] | ||||
|  | ||||
|   ngOnInit(): void { | ||||
|     let filterRuleType: FilterRuleType = FILTER_RULE_TYPES.find(t => t.id == this.filterRuleTypeID) | ||||
|     this.title = filterRuleType.displayName | ||||
|     this.display = filterRuleType.datatype | ||||
|   ngOnInit() { | ||||
|     this.items$.subscribe(result => this.items = result.results) | ||||
|   } | ||||
|  | ||||
|   toggleItem(item: ObjectWithId): void { | ||||
|     this.toggle.emit(item) | ||||
|   } | ||||
|  | ||||
|   isItemSelected(item: ObjectWithId): boolean { | ||||
|     return this.itemsSelected?.find(i => i.id == item.id) !== undefined | ||||
|   } | ||||
|  | ||||
|   dropdownOpenChange(open: boolean): void { | ||||
|     if (open) { | ||||
|       setTimeout(() => { | ||||
|   | ||||
| @@ -3,14 +3,17 @@ | ||||
|     <div class="text-muted mt-1">Filter by:</div> | ||||
|   </div> | ||||
|   <div class="col"> | ||||
|     <input class="form-control form-control-sm" type="text" [(ngModel)]="filterText" placeholder="Title" #filterTextInput> | ||||
|     <input class="form-control form-control-sm" type="text" [(ngModel)]="filterEditorService.filterText" placeholder="Title" #filterTextInput> | ||||
|   </div> | ||||
|  | ||||
|   <app-filter-dropdown class="col-auto" *ngFor="let quickFilterRuleTypeID of quickFilterRuleTypeIDs" [filterRuleTypeID]="quickFilterRuleTypeID" (toggle)="toggleFilterByItem($event, quickFilterRuleTypeID)"></app-filter-dropdown> | ||||
|   <app-filter-dropdown class="col-auto" [(items$)]="filterEditorService.tags$" [itemsSelected]="filterEditorService.selectedTags" [title]="'Tags'" [display]="'tag'" (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-date class="col-auto" *ngFor="let dateAddedFilterRuleTypeID of dateAddedFilterRuleTypeIDs" [filterRuleTypeIDs]="dateAddedFilterRuleTypeID" (selected)="setDateFilter($event)"></app-filter-dropdown-date> | ||||
|   <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> | ||||
|  | ||||
|   <button class="btn btn-link btn-sm" [disabled]="!hasFilters()" (click)="clearSelected()"> | ||||
|   <button class="btn btn-link btn-sm" [disabled]="!filterEditorService.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,14 +1,10 @@ | ||||
| import { Component, EventEmitter, Input, OnInit, Output, ElementRef, AfterViewInit, QueryList, ViewChild, ViewChildren } from '@angular/core'; | ||||
| 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 { Component, EventEmitter, Input, Output, ElementRef, AfterViewInit, QueryList, ViewChild, ViewChildren } from '@angular/core'; | ||||
| import { AbstractPaperlessService } from 'src/app/services/rest/abstract-paperless-service'; | ||||
| import { ObjectWithId } from 'src/app/data/object-with-id'; | ||||
| 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 { AbstractPaperlessService } from 'src/app/services/rest/abstract-paperless-service'; | ||||
| import { ObjectWithId } from 'src/app/data/object-with-id'; | ||||
| import { CorrespondentService } from 'src/app/services/rest/correspondent.service'; | ||||
| import { DocumentTypeService } from 'src/app/services/rest/document-type.service'; | ||||
| import { TagService } from 'src/app/services/rest/tag.service'; | ||||
| import { FilterDropdownComponent } from './filter-dropdown/filter-dropdown.component' | ||||
| import { FilterDropdownDateComponent } from './filter-dropdown-date/filter-dropdown-date.component' | ||||
| import { fromEvent } from 'rxjs'; | ||||
| @@ -20,38 +16,20 @@ import { NgbDateStruct } from '@ng-bootstrap/ng-bootstrap'; | ||||
|   templateUrl: './filter-editor.component.html', | ||||
|   styleUrls: ['./filter-editor.component.scss'] | ||||
| }) | ||||
| export class FilterEditorComponent implements OnInit, AfterViewInit { | ||||
| export class FilterEditorComponent implements AfterViewInit { | ||||
|  | ||||
|   constructor(private documentTypeService: DocumentTypeService, private tagService: TagService, private correspondentService: CorrespondentService) { } | ||||
|   constructor() { } | ||||
|  | ||||
|   @Input() | ||||
|   filterEditorService: FilterEditorViewService | ||||
|  | ||||
|   @Output() | ||||
|   clear = new EventEmitter() | ||||
|  | ||||
|   @Input() | ||||
|   filterRules: FilterRule[] = [] | ||||
|  | ||||
|   @Output() | ||||
|   apply = new EventEmitter() | ||||
|  | ||||
|   @ViewChild('filterTextInput') filterTextInput: ElementRef; | ||||
|   @ViewChildren(FilterDropdownComponent) quickFilterDropdowns!: QueryList<FilterDropdownComponent>; | ||||
|   @ViewChildren(FilterDropdownDateComponent) quickDateFilterDropdowns!: QueryList<FilterDropdownDateComponent>; | ||||
|  | ||||
|   quickFilterRuleTypeIDs: number[] = [FILTER_HAS_TAG, FILTER_CORRESPONDENT, FILTER_DOCUMENT_TYPE] | ||||
|   dateAddedFilterRuleTypeIDs: any[] = [[FILTER_ADDED_BEFORE, FILTER_ADDED_AFTER], [FILTER_CREATED_BEFORE, FILTER_CREATED_AFTER]] | ||||
|  | ||||
|   correspondents: PaperlessCorrespondent[] = [] | ||||
|   tags: PaperlessTag[] = [] | ||||
|   documentTypes: PaperlessDocumentType[] = [] | ||||
|  | ||||
|   filterText: string | ||||
|  | ||||
|   ngOnInit(): void { | ||||
|     this.updateTextFilterInput() | ||||
|     this.tagService.listAll().subscribe(result => this.setDropdownItems(result.results, FILTER_HAS_TAG)) | ||||
|     this.correspondentService.listAll().subscribe(result => this.setDropdownItems(result.results, FILTER_CORRESPONDENT)) | ||||
|     this.documentTypeService.listAll().subscribe(result => this.setDropdownItems(result.results, FILTER_DOCUMENT_TYPE)) | ||||
|   } | ||||
|  | ||||
|   ngAfterViewInit() { | ||||
|     fromEvent(this.filterTextInput.nativeElement,'keyup').pipe( | ||||
| @@ -59,120 +37,52 @@ export class FilterEditorComponent implements OnInit, AfterViewInit { | ||||
|       distinctUntilChanged(), | ||||
|       tap() | ||||
|     ).subscribe((event: Event) => { | ||||
|       this.filterText = (event.target as HTMLInputElement).value | ||||
|       this.onTextFilterInput() | ||||
|       this.filterEditorService.filterText = (event.target as HTMLInputElement).value | ||||
|       this.applyFilters() | ||||
|     }) | ||||
|     this.quickDateFilterDropdowns.forEach(d => this.updateDateDropdown(d)) | ||||
|   } | ||||
|  | ||||
|   setDropdownItems(items: ObjectWithId[], filterRuleTypeID: number): void { | ||||
|     let dropdown: FilterDropdownComponent = this.getDropdownByFilterRuleTypeID(filterRuleTypeID) | ||||
|     if (dropdown) { | ||||
|       dropdown.items = items | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   updateDropdownActiveItems(dropdown: FilterDropdownComponent): void { | ||||
|     let activeRulesValues = this.filterRules.filter(r => r.type.id == dropdown.filterRuleTypeID).map(r => r.value) | ||||
|     let activeItems = [] | ||||
|     if (activeRulesValues.length > 0) { | ||||
|       activeItems = dropdown.items.filter(i => activeRulesValues.includes(i.id)) | ||||
|     } | ||||
|     dropdown.itemsActive = activeItems | ||||
|   } | ||||
|  | ||||
|   updateDateDropdown(dateDropdown: FilterDropdownDateComponent) { | ||||
|     let activeRules = this.filterRules.filter(r => dateDropdown.filterRuleTypeIDs.includes(r.type.id)) | ||||
|     if (activeRules.length > 0) { | ||||
|       activeRules.forEach(rule => { | ||||
|         let date = { year: rule.value.substring(0,4), month: rule.value.substring(5,7), day: rule.value.substring(8,10) } | ||||
|         rule.type.filtervar.indexOf('gt') > -1 ? dateDropdown.dateAfter = date : dateDropdown.dateBefore = date | ||||
|       }) | ||||
|     } else { | ||||
|       dateDropdown.dateAfter = dateDropdown.dateBefore = undefined | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   getDropdownByFilterRuleTypeID(filterRuleTypeID: number): FilterDropdownComponent { | ||||
|     return this.quickFilterDropdowns.find(d => d.filterRuleTypeID == filterRuleTypeID) | ||||
|   } | ||||
|  | ||||
|   applySelected() { | ||||
|   applyFilters() { | ||||
|     this.apply.next() | ||||
|   } | ||||
|  | ||||
|   clearSelected() { | ||||
|     this.filterRules.splice(0,this.filterRules.length) | ||||
|     this.updateTextFilterInput() | ||||
|     this.quickFilterDropdowns.forEach(d => this.updateDropdownActiveItems(d)) | ||||
|     this.quickDateFilterDropdowns.forEach(d => this.updateDateDropdown(d)) | ||||
|     this.filterEditorService.clear() | ||||
|     this.clear.next() | ||||
|   } | ||||
|  | ||||
|   hasFilters() { | ||||
|     return this.filterRules.length > 0 | ||||
|   onToggleTag(tag: PaperlessTag) { | ||||
|     this.filterEditorService.toggleFitlerByTag(tag) | ||||
|     this.applyFilters() | ||||
|   } | ||||
|  | ||||
|   updateTextFilterInput() { | ||||
|     let existingTextRule = this.filterRules.find(rule => rule.type.id == FILTER_TITLE) | ||||
|     if (existingTextRule) this.filterText = existingTextRule.value | ||||
|     else this.filterText = '' | ||||
|   onToggleCorrespondent(correspondent: PaperlessCorrespondent) { | ||||
|     this.filterEditorService.toggleFitlerByCorrespondent(correspondent) | ||||
|     this.applyFilters() | ||||
|   } | ||||
|  | ||||
|   onTextFilterInput() { | ||||
|     let text = this.filterText | ||||
|     let filterRules = this.filterRules | ||||
|     let existingRule = filterRules.find(rule => rule.type.id == FILTER_TITLE) | ||||
|     if (existingRule && existingRule.value == text) { | ||||
|       return | ||||
|     } else if (existingRule) { | ||||
|       existingRule.value = text | ||||
|     } else { | ||||
|       filterRules.push({type: FILTER_RULE_TYPES.find(t => t.id == FILTER_TITLE), value: text}) | ||||
|     } | ||||
|     this.filterRules = filterRules | ||||
|     this.applySelected() | ||||
|   onToggleDocumentType(documentType: PaperlessDocumentType) { | ||||
|     this.filterEditorService.toggleFitlerByDocumentType(documentType) | ||||
|     this.applyFilters() | ||||
|   } | ||||
|  | ||||
|   toggleFilterByItem(item: any, filterRuleTypeID: number) { | ||||
|     let dropdown = this.getDropdownByFilterRuleTypeID(filterRuleTypeID) | ||||
|     if (typeof item == 'number') { | ||||
|       item = dropdown.items.find(i => i.id == item) | ||||
|     } | ||||
|     let filterRules = this.filterRules | ||||
|     let filterRuleType: FilterRuleType = FILTER_RULE_TYPES.find(t => t.id == filterRuleTypeID) | ||||
|     let existingRule = filterRules.find(rule => rule.type.id == filterRuleType.id) | ||||
|  | ||||
|     if (existingRule && existingRule.value == item.id) { | ||||
|       filterRules.splice(filterRules.indexOf(existingRule), 1) | ||||
|     } else if (existingRule && filterRuleType.id == FILTER_HAS_TAG) { | ||||
|       filterRules.push({type: FILTER_RULE_TYPES.find(t => t.id == filterRuleType.id), value: item.id}) | ||||
|     } else if (existingRule && existingRule.value == item.id) { | ||||
|       return | ||||
|     } else if (existingRule) { | ||||
|       existingRule.value = item.id | ||||
|     } else { | ||||
|       filterRules.push({type: FILTER_RULE_TYPES.find(t => t.id == filterRuleType.id), value: item.id}) | ||||
|     } | ||||
|  | ||||
|     this.updateDropdownActiveItems(dropdown) | ||||
|  | ||||
|     this.filterRules = filterRules | ||||
|     this.applySelected() | ||||
|   onDateCreatedBeforeSet(date: NgbDateStruct) { | ||||
|     this.filterEditorService.setDateCreatedBefore(date) | ||||
|     this.applyFilters() | ||||
|   } | ||||
|  | ||||
|   setDateFilter(newFilterRule: FilterRule) { | ||||
|     let filterRules = this.filterRules | ||||
|     let existingRule = filterRules.find(rule => rule.type.id == newFilterRule.type.id) | ||||
|  | ||||
|     if (existingRule) { | ||||
|       existingRule.value = newFilterRule.value | ||||
|     } else { | ||||
|       filterRules.push(newFilterRule) | ||||
|     } | ||||
|  | ||||
|     this.filterRules = filterRules | ||||
|     this.applySelected() | ||||
|   onDateCreatedAfterSet(date: NgbDateStruct) { | ||||
|     this.filterEditorService.setDateCreatedAfter(date) | ||||
|     this.applyFilters() | ||||
|   } | ||||
|  | ||||
|   onDateAddedBeforeSet(date: NgbDateStruct) { | ||||
|     this.filterEditorService.setDateAddedBefore(date) | ||||
|     this.applyFilters() | ||||
|   } | ||||
|  | ||||
|   onDateAddedAfterSet(date: NgbDateStruct) { | ||||
|     this.filterEditorService.setDateAddedAfter(date) | ||||
|     this.applyFilters() | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -9,7 +9,7 @@ import { DocumentService } from './rest/document.service'; | ||||
|  | ||||
| /** | ||||
|  * This service manages the document list which is displayed using the document list view. | ||||
|  *  | ||||
|  * | ||||
|  * This service also serves saved views by transparently switching between the document list | ||||
|  * and saved views on request. See below. | ||||
|  */ | ||||
| @@ -25,7 +25,7 @@ export class DocumentListViewService { | ||||
|   currentPage = 1 | ||||
|   currentPageSize: number = +localStorage.getItem(GENERAL_SETTINGS.DOCUMENT_LIST_SIZE) || GENERAL_SETTINGS.DOCUMENT_LIST_SIZE_DEFAULT | ||||
|   collectionSize: number | ||||
|    | ||||
|  | ||||
|   /** | ||||
|    * This is the current config for the document list. The service will always remember the last settings used for the document list. | ||||
|    */ | ||||
| @@ -192,7 +192,7 @@ export class DocumentListViewService { | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   constructor(private documentService: DocumentService) {  | ||||
|   constructor(private documentService: DocumentService) { | ||||
|     let documentListViewConfigJson = sessionStorage.getItem(DOCUMENT_LIST_SERVICE.CURRENT_VIEW_CONFIG) | ||||
|     if (documentListViewConfigJson) { | ||||
|       try { | ||||
|   | ||||
							
								
								
									
										16
									
								
								src-ui/src/app/services/filter-editor-view.service.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src-ui/src/app/services/filter-editor-view.service.spec.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| 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(); | ||||
|   }); | ||||
| }); | ||||
							
								
								
									
										188
									
								
								src-ui/src/app/services/filter-editor-view.service.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										188
									
								
								src-ui/src/app/services/filter-editor-view.service.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,188 @@ | ||||
| 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$: Observable<Results<PaperlessTag>> | ||||
|   correspondents$: Observable<Results<PaperlessCorrespondent>> | ||||
|   documentTypes$: Observable<Results<PaperlessDocumentType>> | ||||
|  | ||||
|   tags: PaperlessTag[] = [] | ||||
|   correspondents: PaperlessCorrespondent[] | ||||
|   documentTypes: PaperlessDocumentType[] = [] | ||||
|  | ||||
|   filterRules: FilterRule[] = [] | ||||
|  | ||||
|   constructor(private tagService: TagService, private documentTypeService: DocumentTypeService, private correspondentService: CorrespondentService) { | ||||
|     this.tags$ = this.tagService.listAll() | ||||
|     this.tags$.subscribe(result => this.tags = result.results) | ||||
|     this.correspondents$ = this.correspondentService.listAll() | ||||
|     this.correspondents$.subscribe(result => this.correspondents = result.results) | ||||
|     this.documentTypes$ = this.documentTypeService.listAll() | ||||
|     this.documentTypes$.subscribe(result => this.documentTypes = result.results) | ||||
|   } | ||||
|  | ||||
|   clear() { | ||||
|     this.filterRules = [] | ||||
|   } | ||||
|  | ||||
|   hasFilters() { | ||||
|     return this.filterRules.length > 0 | ||||
|   } | ||||
|  | ||||
|   set filterText(text: string) { | ||||
|     let filterRules = this.filterRules | ||||
|     let existingRule = filterRules.find(rule => rule.type.id == FILTER_TITLE) | ||||
|     if (existingRule && existingRule.value == text) { | ||||
|       return | ||||
|     } else if (existingRule) { | ||||
|       existingRule.value = text | ||||
|     } else { | ||||
|       filterRules.push({type: FILTER_RULE_TYPES.find(t => t.id == FILTER_TITLE), value: text}) | ||||
|     } | ||||
|     this.filterRules = filterRules | ||||
|   } | ||||
|  | ||||
|   get filterText(): 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)) | ||||
|   } | ||||
|  | ||||
|   toggleFitlerByTag(tag: PaperlessTag) { | ||||
|     this.toggleFilterByItem(tag, FILTER_HAS_TAG) | ||||
|   } | ||||
|  | ||||
|   toggleFitlerByCorrespondent(tag: PaperlessCorrespondent) { | ||||
|     this.toggleFilterByItem(tag, FILTER_CORRESPONDENT) | ||||
|   } | ||||
|  | ||||
|   toggleFitlerByDocumentType(tag: PaperlessDocumentType) { | ||||
|     this.toggleFilterByItem(tag, FILTER_DOCUMENT_TYPE) | ||||
|   } | ||||
|  | ||||
|   toggleFitlerByTagID(tagID: number) { | ||||
|     this.toggleFitlerByTag(this.tags?.find(t => t.id == tagID)) | ||||
|   } | ||||
|  | ||||
|   toggleFitlerByCorrespondentID(correspondentID: number) { | ||||
|     this.toggleFitlerByCorrespondent(this.correspondents?.find(t => t.id == correspondentID)) | ||||
|   } | ||||
|  | ||||
|   toggleFitlerByDocumentTypeID(documentTypeID: number) { | ||||
|     this.toggleFitlerByDocumentType(this.documentTypes?.find(t => t.id == documentTypeID)) | ||||
|   } | ||||
|  | ||||
|   private toggleFilterByItem(item: ObjectWithId, filterRuleTypeID: number) { | ||||
|     let filterRules = this.filterRules | ||||
|     let filterRuleType: FilterRuleType = FILTER_RULE_TYPES.find(t => t.id == filterRuleTypeID) | ||||
|     let existingRule = filterRules.find(rule => rule.type.id == filterRuleType.id) | ||||
|  | ||||
|     if (existingRule && existingRule.value == item.id) { | ||||
|       filterRules.splice(filterRules.indexOf(existingRule), 1) | ||||
|     } else if (existingRule && filterRuleType.id == FILTER_HAS_TAG) { | ||||
|       filterRules.push({type: FILTER_RULE_TYPES.find(t => t.id == filterRuleType.id), value: item.id}) | ||||
|     } else if (existingRule && existingRule.value == item.id) { | ||||
|       return | ||||
|     } else if (existingRule) { | ||||
|       existingRule.value = item.id | ||||
|     } else { | ||||
|       filterRules.push({type: FILTER_RULE_TYPES.find(t => t.id == filterRuleType.id), 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) { | ||||
|     this.setDate(date, FILTER_CREATED_BEFORE) | ||||
|   } | ||||
|  | ||||
|   setDateCreatedAfter(date: NgbDateStruct) { | ||||
|     this.setDate(date, FILTER_CREATED_AFTER) | ||||
|   } | ||||
|  | ||||
|   setDateAddedBefore(date: NgbDateStruct) { | ||||
|     this.setDate(date, FILTER_ADDED_BEFORE) | ||||
|   } | ||||
|  | ||||
|   setDateAddedAfter(date: NgbDateStruct) { | ||||
|     this.setDate(date, FILTER_ADDED_AFTER) | ||||
|   } | ||||
|  | ||||
|   setDate(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 | ||||
|   } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 Michael Shamoon
					Michael Shamoon