mirror of
				https://github.com/paperless-ngx/paperless-ngx.git
				synced 2025-10-30 03:56:23 -05:00 
			
		
		
		
	| @@ -4,38 +4,39 @@ | |||||||
|   </button> |   </button> | ||||||
|   <div class="dropdown-menu date-filter shadow pt-0" ngbDropdownMenu attr.aria-labelledby="dropdown{{title}}"> |   <div class="dropdown-menu date-filter shadow pt-0" ngbDropdownMenu attr.aria-labelledby="dropdown{{title}}"> | ||||||
|     <div class="list-group list-group-flush"> |     <div class="list-group list-group-flush"> | ||||||
|         <button class="list-group-item small list-goup list-group-item-action d-flex p-2 pl-3" (click)="clear()">Clear</button> |         <button *ngFor="let qf of quickFilters" class="list-group-item small list-goup list-group-item-action d-flex p-2 pl-3" role="menuitem" (click)="setDateQuickFilter(qf.id)"> | ||||||
|         <button *ngFor="let range of [7, 30, 'month', 'year']" class="list-group-item small list-goup list-group-item-action d-flex p-2 pl-3" role="menuitem" (click)="setDateQuickFilter(range)"> |           {{qf.name}} | ||||||
|           <ng-container *ngIf="isStringRange(range)">This </ng-container> |  | ||||||
|           {{ range }} |  | ||||||
|           <ng-container *ngIf="!isStringRange(range)"> days</ng-container> |  | ||||||
|         </button> |         </button> | ||||||
|         <div class="list-group-item d-flex flex-column align-items-start" role="menuitem"> |         <div class="list-group-item d-flex flex-column align-items-start" role="menuitem"> | ||||||
|           <div>Before</div> |  | ||||||
|           <div class="input-group input-group-sm"> |           <div class="mb-2 d-flex flex-row w-100 justify-content-between small"> | ||||||
|             <input class="form-control" type="text" placeholder="yyyy-mm-dd" name="before" [(ngModel)]="_dateBefore" [maxDate]="this._maxDate" ngbDatepicker (dateSelect)="onBeforeSelected($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"> |  | ||||||
|                   <path fill-rule="evenodd" d="M3.5 0a.5.5 0 0 1 .5.5V1h8V.5a.5.5 0 0 1 1 0V1h1a2 2 0 0 1 2 2v11a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V3a2 2 0 0 1 2-2h1V.5a.5.5 0 0 1 .5-.5zM1 4v10a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V4H1z"/> |  | ||||||
|                   <path d="M6.445 11.688V6.354h-.633A12.6 12.6 0 0 0 4.5 7.16v.695c.375-.257.969-.62 1.258-.777h.012v4.61h.675zm1.188-1.305c.047.64.594 1.406 1.703 1.406 1.258 0 2-1.066 2-2.871 0-1.934-.781-2.668-1.953-2.668-.926 0-1.797.672-1.797 1.809 0 1.16.824 1.77 1.676 1.77.746 0 1.23-.376 1.383-.79h.027c-.004 1.316-.461 2.164-1.305 2.164-.664 0-1.008-.45-1.05-.82h-.684zm2.953-2.317c0 .696-.559 1.18-1.184 1.18-.601 0-1.144-.383-1.144-1.2 0-.823.582-1.21 1.168-1.21.633 0 1.16.398 1.16 1.23z"/> |  | ||||||
|                 </svg> |  | ||||||
|               </button> |  | ||||||
|             </div> |  | ||||||
|           </div> |  | ||||||
|         </div> |  | ||||||
|         <div class="list-group-item d-flex flex-column align-items-start" role="menuitem"> |  | ||||||
|             <div>After</div> |             <div>After</div> | ||||||
|           <div class="input-group input-group-sm"> |             <a *ngIf="dateAfter" class="btn btn-link p-0 m-0" (click)="clearAfter()"> | ||||||
|             <input class="form-control form-control-sm" type="text" placeholder="yyyy-mm-dd" name="after" [(ngModel)]="_dateAfter" [maxDate]="this._maxDate" ngbDatepicker (dateSelect)="onAfterSelected($event)" #dpAfter="ngbDatepicker"> |               <svg width="0.8em" height="0.8em" viewBox="0 0 16 16" class="bi bi-x" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> | ||||||
|             <div class="input-group-append"> |                 <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" /> | ||||||
|               <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"> |  | ||||||
|                   <path fill-rule="evenodd" d="M3.5 0a.5.5 0 0 1 .5.5V1h8V.5a.5.5 0 0 1 1 0V1h1a2 2 0 0 1 2 2v11a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V3a2 2 0 0 1 2-2h1V.5a.5.5 0 0 1 .5-.5zM1 4v10a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V4H1z"/> |  | ||||||
|                   <path d="M6.445 11.688V6.354h-.633A12.6 12.6 0 0 0 4.5 7.16v.695c.375-.257.969-.62 1.258-.777h.012v4.61h.675zm1.188-1.305c.047.64.594 1.406 1.703 1.406 1.258 0 2-1.066 2-2.871 0-1.934-.781-2.668-1.953-2.668-.926 0-1.797.672-1.797 1.809 0 1.16.824 1.77 1.676 1.77.746 0 1.23-.376 1.383-.79h.027c-.004 1.316-.461 2.164-1.305 2.164-.664 0-1.008-.45-1.05-.82h-.684zm2.953-2.317c0 .696-.559 1.18-1.184 1.18-.601 0-1.144-.383-1.144-1.2 0-.823.582-1.21 1.168-1.21.633 0 1.16.398 1.16 1.23z"/> |  | ||||||
|               </svg> |               </svg> | ||||||
|               </button> |               <small>Clear</small> | ||||||
|             </div> |             </a> | ||||||
|  |           </div> | ||||||
|  |  | ||||||
|  |           <div class="input-group input-group-sm"> | ||||||
|  |             <input type="date" class="form-control" id="date_after" [(ngModel)]="dateAfter" (change)="onChangeDebounce()"> | ||||||
|  |           </div> | ||||||
|  |         </div> | ||||||
|  |         <div class="list-group-item d-flex flex-column align-items-start" role="menuitem"> | ||||||
|  |  | ||||||
|  |           <div class="mb-2 d-flex flex-row w-100 justify-content-between small"> | ||||||
|  |             <div>Before</div> | ||||||
|  |             <a *ngIf="dateBefore" class="btn btn-link p-0 m-0" (click)="clearBefore()"> | ||||||
|  |               <svg width="0.8em" height="0.8em" 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> | ||||||
|  |               <small>Clear</small> | ||||||
|  |             </a> | ||||||
|  |           </div> | ||||||
|  |  | ||||||
|  |           <div class="input-group input-group-sm"> | ||||||
|  |             <input type="date" class="form-control" id="date_before" [(ngModel)]="dateBefore" (change)="onChangeDebounce()"> | ||||||
|           </div> |           </div> | ||||||
|         </div> |         </div> | ||||||
|     </div> |     </div> | ||||||
|   | |||||||
| @@ -1,24 +1,37 @@ | |||||||
| import { Component, EventEmitter, Input, Output, ElementRef, ViewChild, SimpleChange } from '@angular/core'; | import { formatDate } from '@angular/common'; | ||||||
| import { NgbDate, NgbDateStruct, NgbDatepicker } from '@ng-bootstrap/ng-bootstrap'; | import { Component, EventEmitter, Input, Output, OnInit, OnDestroy } from '@angular/core'; | ||||||
|  | import { Subject, Subscription } from 'rxjs'; | ||||||
|  | import { debounceTime, distinctUntilChanged } from 'rxjs/operators'; | ||||||
|  |  | ||||||
| export interface DateSelection { | export interface DateSelection { | ||||||
|   before?: NgbDateStruct |   before?: string | ||||||
|   after?: NgbDateStruct |   after?: string | ||||||
| } | } | ||||||
|  |  | ||||||
|  | const FILTER_LAST_7_DAYS = 0 | ||||||
|  | const FILTER_LAST_MONTH = 1 | ||||||
|  | const FILTER_LAST_3_MONTHS = 2 | ||||||
|  | const FILTER_LAST_YEAR = 3 | ||||||
|  |  | ||||||
| @Component({ | @Component({ | ||||||
|   selector: 'app-filter-dropdown-date', |   selector: 'app-filter-dropdown-date', | ||||||
|   templateUrl: './filter-dropdown-date.component.html', |   templateUrl: './filter-dropdown-date.component.html', | ||||||
|   styleUrls: ['./filter-dropdown-date.component.scss'] |   styleUrls: ['./filter-dropdown-date.component.scss'] | ||||||
| }) | }) | ||||||
| export class FilterDropdownDateComponent { | export class FilterDropdownDateComponent implements OnInit, OnDestroy { | ||||||
|  |  | ||||||
|  |   quickFilters = [ | ||||||
|  |     {id: FILTER_LAST_7_DAYS, name: "Last 7 days"},  | ||||||
|  |     {id: FILTER_LAST_MONTH, name: "Last month"}, | ||||||
|  |     {id: FILTER_LAST_3_MONTHS, name: "Last 3 months"}, | ||||||
|  |     {id: FILTER_LAST_YEAR, name: "Last year"} | ||||||
|  |   ] | ||||||
|  |  | ||||||
|   @Input() |   @Input() | ||||||
|   dateBefore: NgbDateStruct |   dateBefore: string | ||||||
|  |  | ||||||
|   @Input() |   @Input() | ||||||
|   dateAfter: NgbDateStruct |   dateAfter: string | ||||||
|  |  | ||||||
|   @Input() |   @Input() | ||||||
|   title: string |   title: string | ||||||
| @@ -26,87 +39,65 @@ export class FilterDropdownDateComponent { | |||||||
|   @Output() |   @Output() | ||||||
|   datesSet = new EventEmitter<DateSelection>() |   datesSet = new EventEmitter<DateSelection>() | ||||||
|  |  | ||||||
|   @ViewChild('dpAfter') dpAfter: NgbDatepicker |   private datesSetDebounce$ = new Subject() | ||||||
|   @ViewChild('dpBefore') dpBefore: NgbDatepicker |  | ||||||
|  |  | ||||||
|   _dateBefore: NgbDateStruct |   private sub: Subscription | ||||||
|   _dateAfter: NgbDateStruct |  | ||||||
|    |    | ||||||
|   get _maxDate(): NgbDate { |   ngOnInit() { | ||||||
|  |     this.sub = this.datesSetDebounce$.pipe( | ||||||
|  |       debounceTime(400) | ||||||
|  |     ).subscribe(() => { | ||||||
|  |       this.onChange() | ||||||
|  |     }) | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   ngOnDestroy() { | ||||||
|  |     if (this.sub) { | ||||||
|  |       this.sub.unsubscribe() | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   setDateQuickFilter(qf: number) { | ||||||
|  |     this.dateBefore = null | ||||||
|     let date = new Date() |     let date = new Date() | ||||||
|     return NgbDate.from({year: date.getFullYear(), month: date.getMonth() + 1, day: date.getDate()}) |     switch (qf) { | ||||||
|   } |       case FILTER_LAST_7_DAYS: | ||||||
|  |         date.setDate(date.getDate() - 7) | ||||||
|  |         break; | ||||||
|  |  | ||||||
|   isStringRange(range: any) { |       case FILTER_LAST_MONTH: | ||||||
|     return typeof range == 'string' |         date.setMonth(date.getMonth() - 1) | ||||||
|   } |         break; | ||||||
|  |  | ||||||
|   ngOnChanges(changes: SimpleChange) { |       case FILTER_LAST_3_MONTHS: | ||||||
|     // this is a hacky workaround perhaps because of https://github.com/angular/angular/issues/11097 |         date.setMonth(date.getMonth() - 3) | ||||||
|     let dateString: string = '' |  | ||||||
|     let dateAfterChange: SimpleChange |  | ||||||
|     let dateBeforeChange: SimpleChange |  | ||||||
|     if (changes) { |  | ||||||
|       dateAfterChange = changes['dateAfter'] |  | ||||||
|       dateBeforeChange = changes['dateBefore'] |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (this.dpBefore && this.dpAfter) { |  | ||||||
|       let dpAfterElRef: ElementRef = this.dpAfter['_elRef'] |  | ||||||
|       let dpBeforeElRef: ElementRef = this.dpBefore['_elRef'] |  | ||||||
|  |  | ||||||
|       if (dateAfterChange && dateAfterChange.currentValue) { |  | ||||||
|         let dateAfterDate = dateAfterChange.currentValue as NgbDateStruct |  | ||||||
|         dateString = `${dateAfterDate.year}-${dateAfterDate.month.toString().padStart(2,'0')}-${dateAfterDate.day.toString().padStart(2,'0')}` |  | ||||||
|         dpAfterElRef.nativeElement.value = dateString |  | ||||||
|       } else if (dateBeforeChange && dateBeforeChange.currentValue) { |  | ||||||
|         let dateBeforeDate = dateBeforeChange.currentValue as NgbDateStruct |  | ||||||
|         dateString = `${dateBeforeDate.year}-${dateBeforeDate.month.toString().padStart(2,'0')}-${dateBeforeDate.day.toString().padStart(2,'0')}` |  | ||||||
|         dpBeforeElRef.nativeElement.value = dateString |  | ||||||
|       } else { |  | ||||||
|         dpAfterElRef.nativeElement.value = dateString |  | ||||||
|         dpBeforeElRef.nativeElement.value = dateString |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   setDateQuickFilter(range: any) { |  | ||||||
|     let date = new Date() |  | ||||||
|     let newDate: NgbDateStruct = { year: date.getFullYear(), month: date.getMonth() + 1, day: date.getDate() } |  | ||||||
|     switch (typeof range) { |  | ||||||
|       case 'number': |  | ||||||
|         date.setDate(date.getDate() - range) |  | ||||||
|         newDate.year = date.getFullYear() |  | ||||||
|         newDate.month = date.getMonth() + 1 |  | ||||||
|         newDate.day = date.getDate() |  | ||||||
|         break |         break | ||||||
|  |  | ||||||
|       case 'string': |       case FILTER_LAST_YEAR: | ||||||
|         newDate.day = 1 |         date.setFullYear(date.getFullYear() - 1) | ||||||
|         if (range == 'year') newDate.month = 1 |  | ||||||
|         break |         break | ||||||
|    |    | ||||||
|       default: |  | ||||||
|         break |  | ||||||
|       } |       } | ||||||
|     this._dateAfter = newDate |     this.dateAfter = formatDate(date, 'yyyy-MM-dd', "en-us", "UTC") | ||||||
|     this._dateBefore = null |     this.onChange() | ||||||
|     this.datesSet.emit({after: newDate, before: null}) |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   onBeforeSelected(date: NgbDateStruct) { |   onChange() { | ||||||
|     this._dateBefore = date |     this.datesSet.emit({after: this.dateAfter, before: this.dateBefore}) | ||||||
|     this.datesSet.emit({after: this._dateAfter, before: date}) |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   onAfterSelected(date: NgbDateStruct) { |   onChangeDebounce() { | ||||||
|     this._dateAfter = date |     this.datesSetDebounce$.next({after: this.dateAfter, before: this.dateBefore}) | ||||||
|     this.datesSet.emit({after: date, before: this._dateBefore}) |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   clear() { |   clearBefore() { | ||||||
|     this._dateBefore = null |     this.dateBefore = null; | ||||||
|     this._dateAfter = null |     this.onChange() | ||||||
|     this.datesSet.emit({after: null, before: null}) |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   clearAfter() { | ||||||
|  |     this.dateAfter = null; | ||||||
|  |     this.onChange() | ||||||
|  |   } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -179,54 +179,53 @@ export class FilterEditorComponent implements OnInit, OnDestroy { | |||||||
|     this.applyFilters() |     this.applyFilters() | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   get dateCreatedBefore(): NgbDateStruct { |   get dateCreatedBefore(): string { | ||||||
|     let createdBeforeRule: FilterRule = this.filterRules.find(fr => fr.rule_type == FILTER_CREATED_BEFORE) |     let createdBeforeRule: FilterRule = this.filterRules.find(fr => fr.rule_type == FILTER_CREATED_BEFORE) | ||||||
|     return createdBeforeRule ? this.dateParser.parse(createdBeforeRule.value) : null |     return createdBeforeRule ? createdBeforeRule.value : null | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   get dateCreatedAfter(): NgbDateStruct { |   get dateCreatedAfter(): string { | ||||||
|     let createdAfterRule: FilterRule = this.filterRules.find(fr => fr.rule_type == FILTER_CREATED_AFTER) |     let createdAfterRule: FilterRule = this.filterRules.find(fr => fr.rule_type == FILTER_CREATED_AFTER) | ||||||
|     return createdAfterRule ? this.dateParser.parse(createdAfterRule.value) : null |     return createdAfterRule ? createdAfterRule.value : null | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   get dateAddedBefore(): NgbDateStruct { |   get dateAddedBefore(): string { | ||||||
|     let addedBeforeRule: FilterRule = this.filterRules.find(fr => fr.rule_type == FILTER_ADDED_BEFORE) |     let addedBeforeRule: FilterRule = this.filterRules.find(fr => fr.rule_type == FILTER_ADDED_BEFORE) | ||||||
|     return addedBeforeRule ? this.dateParser.parse(addedBeforeRule.value) : null |     return addedBeforeRule ? addedBeforeRule.value : null | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   get dateAddedAfter(): NgbDateStruct { |   get dateAddedAfter(): string { | ||||||
|     let addedAfterRule: FilterRule = this.filterRules.find(fr => fr.rule_type == FILTER_ADDED_AFTER) |     let addedAfterRule: FilterRule = this.filterRules.find(fr => fr.rule_type == FILTER_ADDED_AFTER) | ||||||
|     return addedAfterRule ? this.dateParser.parse(addedAfterRule.value) : null |     return addedAfterRule ? addedAfterRule.value : null | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   setDateCreatedBefore(date?: NgbDateStruct) { |   setDateCreatedBefore(date?: string) { | ||||||
|     if (date) this.setDateFilter(date, FILTER_CREATED_BEFORE) |     if (date) this.setDateFilter(date, FILTER_CREATED_BEFORE) | ||||||
|     else this.clearDateFilter(FILTER_CREATED_BEFORE) |     else this.clearDateFilter(FILTER_CREATED_BEFORE) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   setDateCreatedAfter(date?: NgbDateStruct) { |   setDateCreatedAfter(date?: string) { | ||||||
|     if (date) this.setDateFilter(date, FILTER_CREATED_AFTER) |     if (date) this.setDateFilter(date, FILTER_CREATED_AFTER) | ||||||
|     else this.clearDateFilter(FILTER_CREATED_AFTER) |     else this.clearDateFilter(FILTER_CREATED_AFTER) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   setDateAddedBefore(date?: NgbDateStruct) { |   setDateAddedBefore(date?: string) { | ||||||
|     if (date) this.setDateFilter(date, FILTER_ADDED_BEFORE) |     if (date) this.setDateFilter(date, FILTER_ADDED_BEFORE) | ||||||
|     else this.clearDateFilter(FILTER_ADDED_BEFORE) |     else this.clearDateFilter(FILTER_ADDED_BEFORE) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   setDateAddedAfter(date?: NgbDateStruct) { |   setDateAddedAfter(date?: string) { | ||||||
|     if (date) this.setDateFilter(date, FILTER_ADDED_AFTER) |     if (date) this.setDateFilter(date, FILTER_ADDED_AFTER) | ||||||
|     else this.clearDateFilter(FILTER_ADDED_AFTER) |     else this.clearDateFilter(FILTER_ADDED_AFTER) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   setDateFilter(date: NgbDateStruct, dateRuleTypeID: number) { |   setDateFilter(date: string, dateRuleTypeID: number) { | ||||||
|     let existingRule = this.filterRules.find(rule => rule.rule_type == dateRuleTypeID) |     let existingRule = this.filterRules.find(rule => rule.rule_type == dateRuleTypeID) | ||||||
|     let newValue = this.dateParser.format(date) |  | ||||||
|  |  | ||||||
|     if (existingRule) { |     if (existingRule) { | ||||||
|       existingRule.value = newValue |       existingRule.value = date | ||||||
|     } else { |     } else { | ||||||
|       this.filterRules.push({rule_type: dateRuleTypeID, value: newValue}) |       this.filterRules.push({rule_type: dateRuleTypeID, value: date}) | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 jonaswinkler
					jonaswinkler