mirror of
				https://github.com/paperless-ngx/paperless-ngx.git
				synced 2025-10-30 03:56:23 -05:00 
			
		
		
		
	Merge remote-tracking branch 'upstream/dev' into feature/dark-mode
This commit is contained in:
		| @@ -21,10 +21,10 @@ const LAST_YEAR = 3 | ||||
| export class DateDropdownComponent implements OnInit, OnDestroy { | ||||
|  | ||||
|   quickFilters = [ | ||||
|     {id: LAST_7_DAYS, name: "Last 7 days"}, | ||||
|     {id: LAST_MONTH, name: "Last month"}, | ||||
|     {id: LAST_3_MONTHS, name: "Last 3 months"}, | ||||
|     {id: LAST_YEAR, name: "Last year"} | ||||
|     {id: LAST_7_DAYS, name: $localize`Last 7 days`}, | ||||
|     {id: LAST_MONTH, name: $localize`Last month`}, | ||||
|     {id: LAST_3_MONTHS, name: $localize`Last 3 months`}, | ||||
|     {id: LAST_YEAR, name: $localize`Last year`} | ||||
|   ] | ||||
|  | ||||
|   @Input() | ||||
|   | ||||
| @@ -142,7 +142,7 @@ export class FilterableDropdownComponent { | ||||
|     if (items) { | ||||
|       this._selectionModel.items = Array.from(items) | ||||
|       this._selectionModel.items.unshift({ | ||||
|         name: "None", | ||||
|         name: $localize`Not assigned`, | ||||
|         id: null | ||||
|       }) | ||||
|     } | ||||
| @@ -195,6 +195,9 @@ export class FilterableDropdownComponent { | ||||
|   @Input() | ||||
|   editing = false | ||||
|  | ||||
|   @Input() | ||||
|   applyOnClose = false | ||||
|  | ||||
|   @Output() | ||||
|   apply = new EventEmitter<ChangedItems>() | ||||
|  | ||||
| @@ -208,7 +211,9 @@ export class FilterableDropdownComponent { | ||||
|   applyClicked() { | ||||
|     if (this.selectionModel.isDirty()) { | ||||
|       this.dropdown.close() | ||||
|       this.apply.emit(this.selectionModel.diff()) | ||||
|       if (!this.applyOnClose) { | ||||
|         this.apply.emit(this.selectionModel.diff()) | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
| @@ -223,6 +228,9 @@ export class FilterableDropdownComponent { | ||||
|       this.open.next() | ||||
|     } else { | ||||
|       this.filterText = '' | ||||
|       if (this.applyOnClose && this.selectionModel.isDirty()) { | ||||
|         this.apply.emit(this.selectionModel.diff()) | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -132,7 +132,7 @@ | ||||
|             <div [ngbNavOutlet]="nav" class="mt-2"></div> | ||||
|  | ||||
|             <button type="button" class="btn btn-outline-secondary" (click)="discard()" i18n>Discard</button>  | ||||
|             <button type="button" class="btn btn-outline-primary" (click)="saveEditNext()" *ngIf="hasNext()" i18n>Save & edit next</button>  | ||||
|             <button type="button" class="btn btn-outline-primary" (click)="saveEditNext()" *ngIf="hasNext()" i18n>Save & next</button>  | ||||
|             <button type="submit" class="btn btn-primary" i18n>Save</button>  | ||||
|         </form> | ||||
|     </div> | ||||
|   | ||||
| @@ -30,6 +30,7 @@ | ||||
|         [items]="tags" | ||||
|         [editing]="true" | ||||
|         [multiple]="true" | ||||
|         [applyOnClose]="applyOnClose" | ||||
|         (open)="openTagsDropdown()" | ||||
|         [(selectionModel)]="tagSelectionModel" | ||||
|         (apply)="setTags($event)"> | ||||
| @@ -37,6 +38,7 @@ | ||||
|       <app-filterable-dropdown class="mr-2 mr-md-3" title="Correspondent" icon="person-fill" | ||||
|         [items]="correspondents" | ||||
|         [editing]="true" | ||||
|         [applyOnClose]="applyOnClose" | ||||
|         (open)="openCorrespondentDropdown()" | ||||
|         [(selectionModel)]="correspondentSelectionModel" | ||||
|         (apply)="setCorrespondents($event)"> | ||||
| @@ -44,6 +46,7 @@ | ||||
|       <app-filterable-dropdown class="mr-2 mr-md-3" title="Document Type" icon="file-earmark-fill" | ||||
|         [items]="documentTypes" | ||||
|         [editing]="true" | ||||
|         [applyOnClose]="applyOnClose" | ||||
|         (open)="openDocumentTypeDropdown()" | ||||
|         [(selectionModel)]="documentTypeSelectionModel" | ||||
|         (apply)="setDocumentTypes($event)"> | ||||
|   | ||||
| @@ -15,6 +15,7 @@ import { ConfirmDialogComponent } from 'src/app/components/common/confirm-dialog | ||||
| import { ChangedItems, FilterableDropdownSelectionModel } from '../../common/filterable-dropdown/filterable-dropdown.component'; | ||||
| import { ToggleableItemState } from '../../common/filterable-dropdown/toggleable-dropdown-button/toggleable-dropdown-button.component'; | ||||
| import { MatchingModel } from 'src/app/data/matching-model'; | ||||
| import { SettingsService, SETTINGS_KEYS } from 'src/app/services/settings.service'; | ||||
|  | ||||
| @Component({ | ||||
|   selector: 'app-bulk-editor', | ||||
| @@ -38,9 +39,13 @@ export class BulkEditorComponent { | ||||
|     public list: DocumentListViewService, | ||||
|     private documentService: DocumentService, | ||||
|     private modalService: NgbModal, | ||||
|     private openDocumentService: OpenDocumentsService | ||||
|     private openDocumentService: OpenDocumentsService, | ||||
|     private settings: SettingsService | ||||
|   ) { } | ||||
|  | ||||
|   applyOnClose: boolean = this.settings.get(SETTINGS_KEYS.BULK_EDIT_APPLY_ON_CLOSE) | ||||
|   showConfirmationDialogs: boolean = this.settings.get(SETTINGS_KEYS.BULK_EDIT_CONFIRMATION_DIALOGS) | ||||
|  | ||||
|   ngOnInit() { | ||||
|     this.tagService.listAll().subscribe(result => this.tags = result.results) | ||||
|     this.correspondentService.listAll().subscribe(result => this.correspondents = result.results) | ||||
| @@ -51,10 +56,10 @@ export class BulkEditorComponent { | ||||
|     return this.documentService.bulkEdit(Array.from(this.list.selected), method, args).pipe( | ||||
|       tap(() => { | ||||
|         this.list.reload() | ||||
|         this.list.reduceSelectionToFilter() | ||||
|         this.list.selected.forEach(id => { | ||||
|           this.openDocumentService.refreshDocument(id) | ||||
|         }) | ||||
|         this.list.selectNone() | ||||
|       }) | ||||
|     ) | ||||
|   } | ||||
| @@ -105,30 +110,39 @@ export class BulkEditorComponent { | ||||
|   setTags(changedTags: ChangedItems) { | ||||
|     if (changedTags.itemsToAdd.length == 0 && changedTags.itemsToRemove.length == 0) return | ||||
|  | ||||
|     let modal = this.modalService.open(ConfirmDialogComponent, {backdrop: 'static'}) | ||||
|     modal.componentInstance.title = $localize`Confirm tags assignment` | ||||
|     if (changedTags.itemsToAdd.length == 1 && changedTags.itemsToRemove.length == 0) { | ||||
|       let tag = changedTags.itemsToAdd[0] | ||||
|       modal.componentInstance.message = $localize`This operation will add the tag ${tag.name} to all ${this.list.selected.size} selected document(s).` | ||||
|     } else if (changedTags.itemsToAdd.length > 1 && changedTags.itemsToRemove.length == 0) { | ||||
|       modal.componentInstance.message = $localize`This operation will add the tags ${this._localizeList(changedTags.itemsToAdd)} to all ${this.list.selected.size} selected document(s).` | ||||
|     } else if (changedTags.itemsToAdd.length == 0 && changedTags.itemsToRemove.length == 1) { | ||||
|       let tag = changedTags.itemsToAdd[0] | ||||
|       modal.componentInstance.message = $localize`This operation will remove the tag ${tag.name} from all ${this.list.selected.size} selected document(s).` | ||||
|     } else if (changedTags.itemsToAdd.length == 0 && changedTags.itemsToRemove.length > 1) { | ||||
|       modal.componentInstance.message = $localize`This operation will remove the tags ${this._localizeList(changedTags.itemsToRemove)} from all ${this.list.selected.size} selected document(s).` | ||||
|     if (this.showConfirmationDialogs) { | ||||
|       let modal = this.modalService.open(ConfirmDialogComponent, {backdrop: 'static'}) | ||||
|       modal.componentInstance.title = $localize`Confirm tags assignment` | ||||
|       if (changedTags.itemsToAdd.length == 1 && changedTags.itemsToRemove.length == 0) { | ||||
|         let tag = changedTags.itemsToAdd[0] | ||||
|         modal.componentInstance.message = $localize`This operation will add the tag ${tag.name} to all ${this.list.selected.size} selected document(s).` | ||||
|       } else if (changedTags.itemsToAdd.length > 1 && changedTags.itemsToRemove.length == 0) { | ||||
|         modal.componentInstance.message = $localize`This operation will add the tags ${this._localizeList(changedTags.itemsToAdd)} to all ${this.list.selected.size} selected document(s).` | ||||
|       } else if (changedTags.itemsToAdd.length == 0 && changedTags.itemsToRemove.length == 1) { | ||||
|         let tag = changedTags.itemsToRemove[0] | ||||
|         modal.componentInstance.message = $localize`This operation will remove the tag ${tag.name} from all ${this.list.selected.size} selected document(s).` | ||||
|       } else if (changedTags.itemsToAdd.length == 0 && changedTags.itemsToRemove.length > 1) { | ||||
|         modal.componentInstance.message = $localize`This operation will remove the tags ${this._localizeList(changedTags.itemsToRemove)} from all ${this.list.selected.size} selected document(s).` | ||||
|       } else { | ||||
|         modal.componentInstance.message = $localize`This operation will add the tags ${this._localizeList(changedTags.itemsToAdd)} and remove the tags ${this._localizeList(changedTags.itemsToRemove)} on all ${this.list.selected.size} selected document(s).` | ||||
|       } | ||||
|        | ||||
|       modal.componentInstance.btnClass = "btn-warning" | ||||
|       modal.componentInstance.btnCaption = $localize`Confirm` | ||||
|       modal.componentInstance.confirmClicked.subscribe(() => { | ||||
|         this.performSetTags(modal, changedTags) | ||||
|       }) | ||||
|     } else { | ||||
|       modal.componentInstance.message = $localize`This operation will add the tags ${this._localizeList(changedTags.itemsToAdd)} and remove the tags ${this._localizeList(changedTags.itemsToRemove)} on all ${this.list.selected.size} selected document(s).` | ||||
|       this.performSetTags(null, changedTags) | ||||
|     } | ||||
|      | ||||
|     modal.componentInstance.btnClass = "btn-warning" | ||||
|     modal.componentInstance.btnCaption = $localize`Confirm` | ||||
|     modal.componentInstance.confirmClicked.subscribe(() => { | ||||
|       this.executeBulkOperation('modify_tags', {"add_tags": changedTags.itemsToAdd.map(t => t.id), "remove_tags": changedTags.itemsToRemove.map(t => t.id)}).subscribe( | ||||
|         response => { | ||||
|           this.tagService.clearCache() | ||||
|   } | ||||
|  | ||||
|   private performSetTags(modal, changedTags: ChangedItems) { | ||||
|     this.executeBulkOperation('modify_tags', {"add_tags": changedTags.itemsToAdd.map(t => t.id), "remove_tags": changedTags.itemsToRemove.map(t => t.id)}).subscribe( | ||||
|       response => { | ||||
|         if (modal) { | ||||
|           modal.close() | ||||
|         }) | ||||
|         } | ||||
|       } | ||||
|     ) | ||||
|   } | ||||
| @@ -136,47 +150,67 @@ export class BulkEditorComponent { | ||||
|   setCorrespondents(changedCorrespondents: ChangedItems) { | ||||
|     if (changedCorrespondents.itemsToAdd.length == 0 && changedCorrespondents.itemsToRemove.length == 0) return | ||||
|  | ||||
|     let modal = this.modalService.open(ConfirmDialogComponent, {backdrop: 'static'}) | ||||
|     modal.componentInstance.title = $localize`Confirm correspondent assignment` | ||||
|     let correspondent = changedCorrespondents.itemsToAdd.length > 0 ? changedCorrespondents.itemsToAdd[0] : null | ||||
|     if (correspondent) { | ||||
|       modal.componentInstance.message = $localize`This operation will assign the correspondent ${correspondent.name} to all ${this.list.selected.size} selected document(s).` | ||||
|  | ||||
|     if (this.showConfirmationDialogs) { | ||||
|       let modal = this.modalService.open(ConfirmDialogComponent, {backdrop: 'static'}) | ||||
|       modal.componentInstance.title = $localize`Confirm correspondent assignment` | ||||
|       if (correspondent) { | ||||
|         modal.componentInstance.message = $localize`This operation will assign the correspondent ${correspondent.name} to all ${this.list.selected.size} selected document(s).` | ||||
|       } else { | ||||
|         modal.componentInstance.message = $localize`This operation will remove the correspondent from all ${this.list.selected.size} selected document(s).` | ||||
|       } | ||||
|       modal.componentInstance.btnClass = "btn-warning" | ||||
|       modal.componentInstance.btnCaption = $localize`Confirm` | ||||
|       modal.componentInstance.confirmClicked.subscribe(() => { | ||||
|         this.performSetCorrespondents(modal, correspondent) | ||||
|       }) | ||||
|     } else { | ||||
|       modal.componentInstance.message = $localize`This operation will remove the correspondent from all ${this.list.selected.size} selected document(s).` | ||||
|       this.performSetCorrespondents(null, correspondent) | ||||
|     } | ||||
|     modal.componentInstance.btnClass = "btn-warning" | ||||
|     modal.componentInstance.btnCaption = $localize`Confirm` | ||||
|     modal.componentInstance.confirmClicked.subscribe(() => { | ||||
|       this.executeBulkOperation('set_correspondent', {"correspondent": correspondent?.id}).subscribe( | ||||
|         response => { | ||||
|           this.correspondentService.clearCache() | ||||
|   } | ||||
|  | ||||
|   private performSetCorrespondents(modal, correspondent: MatchingModel) { | ||||
|     this.executeBulkOperation('set_correspondent', {"correspondent": correspondent ? correspondent.id : null}).subscribe( | ||||
|       response => { | ||||
|         if (modal) { | ||||
|           modal.close() | ||||
|         } | ||||
|       ) | ||||
|     }) | ||||
|       } | ||||
|     ) | ||||
|   } | ||||
|  | ||||
|   setDocumentTypes(changedDocumentTypes: ChangedItems) { | ||||
|     if (changedDocumentTypes.itemsToAdd.length == 0 && changedDocumentTypes.itemsToRemove.length == 0) return | ||||
|  | ||||
|     let modal = this.modalService.open(ConfirmDialogComponent, {backdrop: 'static'}) | ||||
|     modal.componentInstance.title = $localize`Confirm document type assignment` | ||||
|     let documentType = changedDocumentTypes.itemsToAdd.length > 0 ? changedDocumentTypes.itemsToAdd[0] : null | ||||
|     if (documentType) { | ||||
|       modal.componentInstance.message = $localize`This operation will assign the document type ${documentType.name} to all ${this.list.selected.size} selected document(s).` | ||||
|  | ||||
|     if (this.showConfirmationDialogs) { | ||||
|       let modal = this.modalService.open(ConfirmDialogComponent, {backdrop: 'static'}) | ||||
|       modal.componentInstance.title = $localize`Confirm document type assignment` | ||||
|       if (documentType) { | ||||
|         modal.componentInstance.message = $localize`This operation will assign the document type ${documentType.name} to all ${this.list.selected.size} selected document(s).` | ||||
|       } else { | ||||
|         modal.componentInstance.message = $localize`This operation will remove the document type from all ${this.list.selected.size} selected document(s).` | ||||
|       } | ||||
|       modal.componentInstance.btnClass = "btn-warning" | ||||
|       modal.componentInstance.btnCaption = $localize`Confirm` | ||||
|       modal.componentInstance.confirmClicked.subscribe(() => { | ||||
|         this.performSetDocumentTypes(modal, documentType) | ||||
|       }) | ||||
|     } else { | ||||
|       modal.componentInstance.message = $localize`This operation will remove the document type from all ${this.list.selected.size} selected document(s).` | ||||
|       this.performSetDocumentTypes(null, documentType) | ||||
|     } | ||||
|     modal.componentInstance.btnClass = "btn-warning" | ||||
|     modal.componentInstance.btnCaption = $localize`Confirm` | ||||
|     modal.componentInstance.confirmClicked.subscribe(() => { | ||||
|       this.executeBulkOperation('set_document_type', {"document_type": documentType?.id}).subscribe( | ||||
|         response => { | ||||
|           this.documentService.clearCache() | ||||
|   } | ||||
|  | ||||
|   private performSetDocumentTypes(modal, documentType) { | ||||
|     this.executeBulkOperation('set_document_type', {"document_type": documentType ? documentType.id : null}).subscribe( | ||||
|       response => { | ||||
|         if (modal) { | ||||
|           modal.close() | ||||
|         } | ||||
|       ) | ||||
|     }) | ||||
|       } | ||||
|     ) | ||||
|   } | ||||
|  | ||||
|   applyDelete() { | ||||
|   | ||||
| @@ -84,7 +84,7 @@ | ||||
|  | ||||
| <div class="d-flex justify-content-between align-items-center"> | ||||
|   <p i18n *ngIf="list.selected.size > 0">Selected {{list.selected.size}} of {{list.collectionSize || 0}} {list.collectionSize, plural, =1 {document} other {documents}}</p> | ||||
|   <p i18n *ngIf="list.selected.size == 0">{{list.collectionSize || 0}} {list.collectionSize, plural, =1 {document} other {documents}}</p> | ||||
|   <p *ngIf="list.selected.size == 0">{list.collectionSize, plural, =1 {1 document} other {{{list.collectionSize || 0}} documents}}</p> | ||||
|   <ngb-pagination [pageSize]="list.currentPageSize" [collectionSize]="list.collectionSize" [(page)]="list.currentPage" [maxSize]="5" | ||||
|   [rotate]="true" (pageChange)="list.reload()" aria-label="Default pagination"></ngb-pagination> | ||||
| </div> | ||||
|   | ||||
| @@ -62,6 +62,10 @@ export class FilterEditorComponent implements OnInit, OnDestroy { | ||||
|  | ||||
|   @Input() | ||||
|   set filterRules (value: FilterRule[]) { | ||||
|     this.documentTypeSelectionModel.clear(false) | ||||
|     this.tagSelectionModel.clear(false) | ||||
|     this.correspondentSelectionModel.clear(false) | ||||
|  | ||||
|     value.forEach(rule => { | ||||
|       switch (rule.rule_type) { | ||||
|         case FILTER_TITLE: | ||||
| @@ -80,22 +84,22 @@ export class FilterEditorComponent implements OnInit, OnDestroy { | ||||
|           this.dateAddedBefore = rule.value | ||||
|           break | ||||
|         case FILTER_HAS_TAG: | ||||
|           this.tagSelectionModel.set(+rule.value, ToggleableItemState.Selected, false) | ||||
|           this.tagSelectionModel.set(rule.value ? +rule.value : null, ToggleableItemState.Selected, false) | ||||
|           break | ||||
|         case FILTER_HAS_ANY_TAG: | ||||
|           this.tagSelectionModel.set(null, ToggleableItemState.Selected, false) | ||||
|           break | ||||
|         case FILTER_CORRESPONDENT: | ||||
|           this.correspondentSelectionModel.set(+rule.value, ToggleableItemState.Selected, false) | ||||
|           this.correspondentSelectionModel.set(rule.value ? +rule.value : null, ToggleableItemState.Selected, false) | ||||
|           break | ||||
|         case FILTER_DOCUMENT_TYPE: | ||||
|           this.documentTypeSelectionModel.set(+rule.value, ToggleableItemState.Selected, false) | ||||
|           this.documentTypeSelectionModel.set(rule.value ? +rule.value : null, ToggleableItemState.Selected, false) | ||||
|           break | ||||
|       } | ||||
|     }) | ||||
|   } | ||||
|  | ||||
|   @Output() | ||||
|   filterRulesChange = new EventEmitter<FilterRule[]>() | ||||
|  | ||||
|   updateRules() { | ||||
|   get filterRules() { | ||||
|     let filterRules: FilterRule[] = [] | ||||
|     if (this._titleFilter) { | ||||
|       filterRules.push({rule_type: FILTER_TITLE, value: this._titleFilter}) | ||||
| @@ -125,7 +129,14 @@ export class FilterEditorComponent implements OnInit, OnDestroy { | ||||
|     if (this.dateAddedAfter) { | ||||
|       filterRules.push({rule_type: FILTER_ADDED_AFTER, value: this.dateAddedAfter}) | ||||
|     } | ||||
|     this.filterRulesChange.next(filterRules) | ||||
|     return filterRules | ||||
|   } | ||||
|  | ||||
|   @Output() | ||||
|   filterRulesChange = new EventEmitter<FilterRule[]>() | ||||
|  | ||||
|   updateRules() { | ||||
|     this.filterRulesChange.next(this.filterRules) | ||||
|   } | ||||
|  | ||||
|   hasFilters() { | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|   </div> | ||||
|   <div class="modal-body"> | ||||
|     <app-input-text i18n-title title="Name" formControlName="name"></app-input-text> | ||||
|     <app-input-check i18n-title title="Show in side bar" formControlName="showInSideBar"></app-input-check> | ||||
|     <app-input-check i18n-title title="Show in sidebar" formControlName="showInSideBar"></app-input-check> | ||||
|     <app-input-check i18n-title title="Show on dashboard" formControlName="showOnDashboard"></app-input-check> | ||||
|   </div> | ||||
|   <div class="modal-footer"> | ||||
|   | ||||
| @@ -9,8 +9,8 @@ | ||||
|      | ||||
|     <app-input-text i18n-title title="Name" formControlName="name"></app-input-text> | ||||
|     <app-input-select i18n-title title="Matching algorithm" [items]="getMatchingAlgorithms()" formControlName="matching_algorithm"></app-input-select> | ||||
|     <app-input-text i18n-title title="Match" formControlName="match" hint="Auto matching does not require you to fill in this field."></app-input-text> | ||||
|     <app-input-check i18n-title title="Case insensitive" formControlName="is_insensitive" hint="Auto matching ignores this option."></app-input-check> | ||||
|     <app-input-text i18n-title title="Match" formControlName="match" i18n-hint hint="Auto matching does not require you to fill in this field."></app-input-text> | ||||
|     <app-input-check i18n-title title="Case insensitive" formControlName="is_insensitive" i18n-hint hint="Auto matching ignores this option."></app-input-check> | ||||
|   </div> | ||||
|   <div class="modal-footer"> | ||||
|     <button type="button" class="btn btn-outline-dark" (click)="cancel()" i18n>Cancel</button> | ||||
|   | ||||
| @@ -9,8 +9,8 @@ | ||||
|        | ||||
|       <app-input-text i18n-title title="Name" formControlName="name"></app-input-text> | ||||
|       <app-input-select i18n-title title="Matching algorithm" [items]="getMatchingAlgorithms()" formControlName="matching_algorithm"></app-input-select> | ||||
|       <app-input-text i18n-title title="Match" formControlName="match" hint="Auto matching does not require you to fill in this field."></app-input-text> | ||||
|       <app-input-check i18n-title title="Case insensitive" formControlName="is_insensitive" hint="Auto matching ignores this option."></app-input-check> | ||||
|       <app-input-text i18n-title title="Match" formControlName="match" i18n-hint hint="Auto matching does not require you to fill in this field."></app-input-text> | ||||
|       <app-input-check i18n-title title="Case insensitive" formControlName="is_insensitive" i18n-hint hint="Auto matching ignores this option."></app-input-check> | ||||
|  | ||||
|     </div> | ||||
|     <div class="modal-footer"> | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| <app-page-header title="Document types"> | ||||
| <app-page-header title="Document types" i18n-title> | ||||
|   <button type="button" class="btn btn-sm btn-outline-primary" (click)="openCreateDialog()" i18n>Create</button> | ||||
| </app-page-header> | ||||
|  | ||||
|   | ||||
| @@ -39,8 +39,15 @@ | ||||
|               <label class="custom-control-label" for="darkModeEnabled">Enabled</label> | ||||
|             </div> | ||||
|           </div> | ||||
|  | ||||
|    | ||||
|         </div> | ||||
|  | ||||
|         <h4 i18n>Bulk editing</h4> | ||||
|  | ||||
|         <app-input-check i18n-title title="Show confirmation dialogs" formControlName="bulkEditConfirmationDialogs" i18n-hint hint="Deleting documents will always ask for confirmation."></app-input-check> | ||||
|         <app-input-check i18n-title title="Apply on close" formControlName="bulkEditApplyOnClose"></app-input-check> | ||||
|  | ||||
|       </ng-template> | ||||
|     </li> | ||||
|     <li [ngbNavItem]="2"> | ||||
|   | ||||
| @@ -1,9 +1,9 @@ | ||||
| import { Component, OnInit, Renderer2  } from '@angular/core'; | ||||
| import { FormControl, FormGroup } from '@angular/forms'; | ||||
| import { PaperlessSavedView } from 'src/app/data/paperless-saved-view'; | ||||
| import { GENERAL_SETTINGS } from 'src/app/data/storage-keys'; | ||||
| import { DocumentListViewService } from 'src/app/services/document-list-view.service'; | ||||
| import { SavedViewService } from 'src/app/services/rest/saved-view.service'; | ||||
| import { SettingsService, SETTINGS_KEYS } from 'src/app/services/settings.service'; | ||||
| import { ToastService } from 'src/app/services/toast.service'; | ||||
| import { AppViewService } from 'src/app/services/app-view.service'; | ||||
|  | ||||
| @@ -17,13 +17,11 @@ export class SettingsComponent implements OnInit { | ||||
|   savedViewGroup = new FormGroup({}) | ||||
|  | ||||
|   settingsForm = new FormGroup({ | ||||
|     'documentListItemPerPage': new FormControl(+localStorage.getItem(GENERAL_SETTINGS.DOCUMENT_LIST_SIZE) || GENERAL_SETTINGS.DOCUMENT_LIST_SIZE_DEFAULT), | ||||
|     'darkModeUseSystem': new FormControl( | ||||
|       localStorage.getItem(GENERAL_SETTINGS.DARK_MODE_USE_SYSTEM) == undefined ? GENERAL_SETTINGS.DARK_MODE_USE_SYSTEM_DEFAULT : JSON.parse(localStorage.getItem(GENERAL_SETTINGS.DARK_MODE_USE_SYSTEM)) | ||||
|     ), | ||||
|     'darkModeEnabled': new FormControl( | ||||
|       localStorage.getItem(GENERAL_SETTINGS.DARK_MODE_ENABLED) == undefined ? GENERAL_SETTINGS.DARK_MODE_ENABLED_DEFAULT : JSON.parse(localStorage.getItem(GENERAL_SETTINGS.DARK_MODE_ENABLED)) | ||||
|     ), | ||||
|     'bulkEditConfirmationDialogs': new FormControl(this.settings.get(SETTINGS_KEYS.BULK_EDIT_CONFIRMATION_DIALOGS)), | ||||
|     'bulkEditApplyOnClose': new FormControl(this.settings.get(SETTINGS_KEYS.BULK_EDIT_APPLY_ON_CLOSE)), | ||||
|     'documentListItemPerPage': new FormControl(this.settings.get(SETTINGS_KEYS.DOCUMENT_LIST_SIZE)), | ||||
|     'darkModeUseSystem': new FormControl(this.settings.get(SETTINGS_KEYS.DARK_MODE_USE_SYSTEM)), | ||||
|     'darkModeEnabled': new FormControl(this.settings.get(SETTINGS_KEYS.DARK_MODE_ENABLED)), | ||||
|     'savedViews': this.savedViewGroup | ||||
|   }) | ||||
|  | ||||
| @@ -33,6 +31,7 @@ export class SettingsComponent implements OnInit { | ||||
|     public savedViewService: SavedViewService, | ||||
|     private documentListViewService: DocumentListViewService, | ||||
|     private toastService: ToastService, | ||||
|     private settings: SettingsService, | ||||
|     private appViewService: AppViewService | ||||
|   ) { } | ||||
|  | ||||
| @@ -67,9 +66,11 @@ export class SettingsComponent implements OnInit { | ||||
|   } | ||||
|  | ||||
|   private saveLocalSettings() { | ||||
|     localStorage.setItem(GENERAL_SETTINGS.DOCUMENT_LIST_SIZE, this.settingsForm.value.documentListItemPerPage) | ||||
|     localStorage.setItem(GENERAL_SETTINGS.DARK_MODE_USE_SYSTEM, this.settingsForm.value.darkModeUseSystem) | ||||
|     localStorage.setItem(GENERAL_SETTINGS.DARK_MODE_ENABLED, (this.settingsForm.value.darkModeEnabled == true).toString()) | ||||
|     this.settings.set(SETTINGS_KEYS.BULK_EDIT_APPLY_ON_CLOSE, this.settingsForm.value.bulkEditApplyOnClose) | ||||
|     this.settings.set(SETTINGS_KEYS.BULK_EDIT_CONFIRMATION_DIALOGS, this.settingsForm.value.bulkEditConfirmationDialogs) | ||||
|     this.settings.set(SETTINGS_KEYS.DOCUMENT_LIST_SIZE, this.settingsForm.value.documentListItemPerPage) | ||||
|     this.settings.set(SETTINGS_KEYS.DARK_MODE_USE_SYSTEM, this.settingsForm.value.darkModeUseSystem) | ||||
|     this.settings.set(SETTINGS_KEYS.DARK_MODE_ENABLED, (this.settingsForm.value.darkModeEnabled == true).toString()) | ||||
|     this.documentListViewService.updatePageSize() | ||||
|     this.appViewService.updateDarkModeSettings() | ||||
|     this.toastService.showInfo($localize`Settings saved successfully.`) | ||||
|   | ||||
| @@ -10,7 +10,7 @@ | ||||
|  | ||||
|  | ||||
|       <div class="form-group paperless-input-select"> | ||||
|         <label for="colour">Colour</label> | ||||
|         <label for="colour" i18n>Color</label> | ||||
|         <ng-select name="colour" formControlName="colour" [items]="getColours()" bindValue="id" bindLabel="name" [clearable]="false"> | ||||
|           <ng-template ng-option-tmp ng-label-tmp let-item="item"> | ||||
|             <span class="badge" [style.background]="item.value" [style.color]="item.textColor">{{item.name}}</span> | ||||
| @@ -18,13 +18,13 @@ | ||||
|         </ng-select> | ||||
|       </div> | ||||
|       | ||||
|       <app-input-check title="Inbox tag" formControlName="is_inbox_tag" hint="Inbox tags are automatically assigned to all consumed documents."></app-input-check> | ||||
|       <app-input-select title="Matching algorithm" [items]="getMatchingAlgorithms()" formControlName="matching_algorithm"></app-input-select> | ||||
|       <app-input-text title="Match" formControlName="match" hint="Auto matching does not require you to fill in this field."></app-input-text> | ||||
|       <app-input-check title="Case insensitive" formControlName="is_insensitive" hint="Auto matching ignores this option."></app-input-check> | ||||
|       <app-input-check i18n-title title="Inbox tag" formControlName="is_inbox_tag" i18n-hint hint="Inbox tags are automatically assigned to all consumed documents."></app-input-check> | ||||
|       <app-input-select i18n-title title="Matching algorithm" [items]="getMatchingAlgorithms()" formControlName="matching_algorithm"></app-input-select> | ||||
|       <app-input-text i18n-title title="Match" formControlName="match" i18n-hint hint="Auto matching does not require you to fill in this field."></app-input-text> | ||||
|       <app-input-check i18n-title title="Case insensitive" formControlName="is_insensitive" i18n-hint hint="Auto matching ignores this option."></app-input-check> | ||||
|     </div> | ||||
|     <div class="modal-footer"> | ||||
|       <button type="button" class="btn btn-outline-dark" (click)="cancel()">Cancel</button> | ||||
|       <button type="submit" class="btn btn-primary">Save</button> | ||||
|       <button type="button" class="btn btn-outline-dark" (click)="cancel()" i18n>Cancel</button> | ||||
|       <button type="submit" class="btn btn-primary" i18n>Save</button> | ||||
|     </div> | ||||
|   </form> | ||||
|   | ||||
| @@ -1,7 +1,5 @@ | ||||
| <app-page-header title="Tags"> | ||||
|   <button type="button" class="btn btn-sm btn-outline-primary" (click)="openCreateDialog()"> | ||||
|     Create | ||||
|   </button> | ||||
| <app-page-header title="Tags" i18n-title> | ||||
|   <button type="button" class="btn btn-sm btn-outline-primary" (click)="openCreateDialog()" i18n>Create</button> | ||||
| </app-page-header> | ||||
|  | ||||
| <div class="row m-0 justify-content-end"> | ||||
| @@ -12,11 +10,11 @@ | ||||
| <table class="table table-striped border shadow-sm"> | ||||
|   <thead> | ||||
|     <tr> | ||||
|       <th scope="col" sortable="name" (sort)="onSort($event)">Name</th> | ||||
|       <th scope="col">Colour</th> | ||||
|       <th scope="col" sortable="matching_algorithm" (sort)="onSort($event)">Matching</th> | ||||
|       <th scope="col" sortable="document_count" (sort)="onSort($event)">Document count</th> | ||||
|       <th scope="col">Actions</th> | ||||
|       <th scope="col" sortable="name" (sort)="onSort($event)" i18n>Name</th> | ||||
|       <th scope="col" i18n>Color</th> | ||||
|       <th scope="col" sortable="matching_algorithm" (sort)="onSort($event)" i18n>Matching</th> | ||||
|       <th scope="col" sortable="document_count" (sort)="onSort($event)" i18n>Document count</th> | ||||
|       <th scope="col" i18n>Actions</th> | ||||
|     </tr> | ||||
|   </thead> | ||||
|   <tbody> | ||||
| @@ -31,21 +29,18 @@ | ||||
|           <button class="btn btn-sm btn-outline-secondary" (click)="filterDocuments(tag)"> | ||||
|             <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-funnel" viewBox="0 0 16 16"> | ||||
|               <path fill-rule="evenodd" d="M1.5 1.5A.5.5 0 0 1 2 1h12a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-.128.334L10 8.692V13.5a.5.5 0 0 1-.342.474l-3 1A.5.5 0 0 1 6 14.5V8.692L1.628 3.834A.5.5 0 0 1 1.5 3.5v-2zm1 .5v1.308l4.372 4.858A.5.5 0 0 1 7 8.5v5.306l2-.666V8.5a.5.5 0 0 1 .128-.334L13.5 3.308V2h-11z"/> | ||||
|             </svg> | ||||
|             Documents | ||||
|             </svg> <ng-container i18n>Documents</ng-container> | ||||
|           </button> | ||||
|           <button class="btn btn-sm btn-outline-secondary" (click)="openEditDialog(tag)"> | ||||
|             <svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-pencil" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> | ||||
|               <path fill-rule="evenodd" d="M12.146.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1 0 .708l-10 10a.5.5 0 0 1-.168.11l-5 2a.5.5 0 0 1-.65-.65l2-5a.5.5 0 0 1 .11-.168l10-10zM11.207 2.5L13.5 4.793 14.793 3.5 12.5 1.207 11.207 2.5zm1.586 3L10.5 3.207 4 9.707V10h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.293l6.5-6.5zm-9.761 5.175l-.106.106-1.528 3.821 3.821-1.528.106-.106A.5.5 0 0 1 5 12.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.468-.325z"/> | ||||
|             </svg> | ||||
|             Edit | ||||
|             </svg> <ng-container i18n>Edit</ng-container> | ||||
|           </button> | ||||
|           <button class="btn btn-sm btn-outline-danger" (click)="openDeleteDialog(tag)"> | ||||
|             <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-trash" viewBox="0 0 16 16"> | ||||
|               <path d="M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0V6z"/> | ||||
|               <path fill-rule="evenodd" d="M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1 1v1zM4.118 4L4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V4.059L11.882 4H4.118zM2.5 3V2h11v1h-11z"/> | ||||
|             </svg> | ||||
|             Delete | ||||
|             </svg> <ng-container i18n>Delete</ng-container> | ||||
|           </button> | ||||
|         </div> | ||||
|       </td> | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| <app-page-header title="Search results"> | ||||
| <app-page-header i18n-title title="Search results"> | ||||
| </app-page-header> | ||||
|  | ||||
| <div *ngIf="errorMessage" class="alert alert-danger" i18n>Invalid search query: {{errorMessage}}</div> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Michael Shamoon
					Michael Shamoon