mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-07-28 18:24:38 -05:00
Saved views, some refactoring
This commit is contained in:
@@ -1,74 +1,83 @@
|
||||
<app-page-header title="Documents">
|
||||
<app-page-header [title]="docs.viewConfig ? docs.viewConfig.title : 'Documents'">
|
||||
|
||||
<div class="btn-group btn-group-toggle mr-2" ngbRadioGroup [(ngModel)]="displayMode" (ngModelChange)="saveDisplayMode()">
|
||||
<div class="btn-group btn-group-toggle mr-2" ngbRadioGroup [(ngModel)]="displayMode"
|
||||
(ngModelChange)="saveDisplayMode()">
|
||||
<label ngbButtonLabel class="btn-outline-secondary btn-sm">
|
||||
<input ngbButton type="radio" class="btn btn-sm" value="details">
|
||||
<svg class="toolbaricon" fill="currentColor">
|
||||
<use xlink:href="assets/bootstrap-icons.svg#list-ul"/>
|
||||
<use xlink:href="assets/bootstrap-icons.svg#list-ul" />
|
||||
</svg>
|
||||
</label>
|
||||
<label ngbButtonLabel class="btn-outline-secondary btn-sm">
|
||||
<input ngbButton type="radio" class="btn btn-sm" value="smallCards">
|
||||
<svg class="toolbaricon" fill="currentColor">
|
||||
<use xlink:href="assets/bootstrap-icons.svg#grid"/>
|
||||
<use xlink:href="assets/bootstrap-icons.svg#grid" />
|
||||
</svg>
|
||||
</label>
|
||||
<label ngbButtonLabel class="btn-outline-secondary btn-sm">
|
||||
<input ngbButton type="radio" class="btn btn-sm" value="largeCards">
|
||||
<svg class="toolbaricon" fill="currentColor">
|
||||
<use xlink:href="assets/bootstrap-icons.svg#hdd-stack"/>
|
||||
<use xlink:href="assets/bootstrap-icons.svg#hdd-stack" />
|
||||
</svg>
|
||||
</label>
|
||||
</div>
|
||||
<div class="btn-group btn-group-toggle mr-2" ngbRadioGroup [(ngModel)]="docs.currentSortDirection" (ngModelChange)="reload()">
|
||||
<div class="btn-group btn-group-toggle mr-2" ngbRadioGroup [(ngModel)]="docs.currentSortDirection"
|
||||
(ngModelChange)="reload()"
|
||||
*ngIf="!docs.viewConfig">
|
||||
<div ngbDropdown class="btn-group">
|
||||
<button class="btn btn-outline-secondary btn-sm" id="dropdownBasic1" ngbDropdownToggle>Sort by</button>
|
||||
<div ngbDropdownMenu aria-labelledby="dropdownBasic1">
|
||||
<button *ngFor="let f of getSortFields()" ngbDropdownItem (click)="setSort(f.field)" [class.active]="docs.currentSortField == f.field">{{f.name}}</button>
|
||||
<button *ngFor="let f of getSortFields()" ngbDropdownItem (click)="setSort(f.field)"
|
||||
[class.active]="docs.currentSortField == f.field">{{f.name}}</button>
|
||||
</div>
|
||||
</div>
|
||||
<label ngbButtonLabel class="btn-outline-secondary btn-sm">
|
||||
<input ngbButton type="radio" class="btn btn-sm" value="asc">
|
||||
<svg class="toolbaricon" fill="currentColor">
|
||||
<use xlink:href="assets/bootstrap-icons.svg#sort-alpha-down"/>
|
||||
<use xlink:href="assets/bootstrap-icons.svg#sort-alpha-down" />
|
||||
</svg>
|
||||
</label>
|
||||
<label ngbButtonLabel class="btn-outline-secondary btn-sm">
|
||||
<input ngbButton type="radio" class="btn btn-sm" value="des">
|
||||
<svg class="toolbaricon" fill="currentColor">
|
||||
<use xlink:href="assets/bootstrap-icons.svg#sort-alpha-up-alt"/>
|
||||
<use xlink:href="assets/bootstrap-icons.svg#sort-alpha-up-alt" />
|
||||
</svg>
|
||||
</label>
|
||||
</div>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary dropdown-toggle" (click)="showFilter=!showFilter">
|
||||
<svg class="toolbaricon" fill="currentColor">
|
||||
<use xlink:href="assets/bootstrap-icons.svg#funnel"/>
|
||||
</svg>
|
||||
Filter
|
||||
</button>
|
||||
<div class="btn-group" *ngIf="!docs.viewConfig">
|
||||
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary" (click)="showFilter=!showFilter">
|
||||
<svg class="toolbaricon" fill="currentColor">
|
||||
<use xlink:href="assets/bootstrap-icons.svg#funnel" />
|
||||
</svg>
|
||||
Filter
|
||||
</button>
|
||||
|
||||
<div class="btn-group" ngbDropdown role="group">
|
||||
<button class="btn btn-sm btn-outline-secondary dropdown-toggle-split" ngbDropdownToggle></button>
|
||||
<div class="dropdown-menu" ngbDropdownMenu>
|
||||
<button ngbDropdownItem *ngFor="let config of savedViewConfigService.getConfigs()" (click)="loadViewConfig(config)">{{config.title}}</button>
|
||||
<div class="dropdown-divider" *ngIf="savedViewConfigService.getConfigs().length > 0"></div>
|
||||
<button ngbDropdownItem (click)="saveViewConfig()">Save current view</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</app-page-header>
|
||||
|
||||
<div class="card w-100 mb-3" [hidden]="!showFilter">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Filter</h5>
|
||||
<app-filter-editor [(ruleSet)]="filter" (apply)="applyFilter()"></app-filter-editor>
|
||||
<app-filter-editor [(filterRules)]="filterRules" (apply)="applyFilterRules()"></app-filter-editor>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ngb-pagination
|
||||
[pageSize]="25"
|
||||
[collectionSize]="docs.collectionSize"
|
||||
[(page)]="docs.currentPage"
|
||||
[maxSize]="5"
|
||||
[rotate]="true"
|
||||
[boundaryLinks]="true"
|
||||
(pageChange)="reload()"
|
||||
aria-label="Default pagination"></ngb-pagination>
|
||||
<ngb-pagination [pageSize]="25" [collectionSize]="docs.collectionSize" [(page)]="docs.currentPage" [maxSize]="5"
|
||||
[rotate]="true" [boundaryLinks]="true" (pageChange)="reload()" aria-label="Default pagination"></ngb-pagination>
|
||||
|
||||
<div *ngIf="displayMode == 'largeCards'">
|
||||
<app-document-card-large *ngFor="let d of docs.documents"
|
||||
[document]="d"
|
||||
[details]="d.content">
|
||||
<app-document-card-large *ngFor="let d of docs.documents" [document]="d" [details]="d.content">
|
||||
</app-document-card-large>
|
||||
</div>
|
||||
|
||||
|
@@ -1,6 +1,11 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { cloneFilterRules, FilterRule } from 'src/app/data/filter-rule';
|
||||
import { SavedViewConfig } from 'src/app/data/saved-view-config';
|
||||
import { DocumentListViewService } from 'src/app/services/document-list-view.service';
|
||||
import { FilterRuleSet } from '../filter-editor/filter-editor.component';
|
||||
import { SavedViewConfigService } from 'src/app/services/saved-view-config.service';
|
||||
import { SaveViewConfigDialogComponent } from './save-view-config-dialog/save-view-config-dialog.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-document-list',
|
||||
@@ -10,11 +15,14 @@ import { FilterRuleSet } from '../filter-editor/filter-editor.component';
|
||||
export class DocumentListComponent implements OnInit {
|
||||
|
||||
constructor(
|
||||
public docs: DocumentListViewService) { }
|
||||
public docs: DocumentListViewService,
|
||||
public savedViewConfigService: SavedViewConfigService,
|
||||
public route: ActivatedRoute,
|
||||
public modalService: NgbModal) { }
|
||||
|
||||
displayMode = 'smallCards' // largeCards, smallCards, details
|
||||
|
||||
filter = new FilterRuleSet()
|
||||
filterRules: FilterRule[] = []
|
||||
showFilter = false
|
||||
|
||||
getSortFields() {
|
||||
@@ -34,18 +42,47 @@ export class DocumentListComponent implements OnInit {
|
||||
if (localStorage.getItem('document-list:displayMode') != null) {
|
||||
this.displayMode = localStorage.getItem('document-list:displayMode')
|
||||
}
|
||||
this.filter = this.docs.currentFilter.clone()
|
||||
this.showFilter = this.filter.rules.length > 0
|
||||
this.reload()
|
||||
this.route.paramMap.subscribe(params => {
|
||||
if (params.has('id')) {
|
||||
this.docs.viewConfig = this.savedViewConfigService.getConfig(params.get('id'))
|
||||
} else {
|
||||
this.filterRules = cloneFilterRules(this.docs.currentFilterRules)
|
||||
this.showFilter = this.filterRules.length > 0
|
||||
this.docs.viewConfig = null
|
||||
}
|
||||
this.reload()
|
||||
})
|
||||
}
|
||||
|
||||
reload() {
|
||||
this.docs.reload()
|
||||
}
|
||||
|
||||
applyFilter() {
|
||||
this.docs.setFilter(this.filter.clone())
|
||||
applyFilterRules() {
|
||||
this.docs.setFilterRules(this.filterRules)
|
||||
this.reload()
|
||||
}
|
||||
|
||||
loadViewConfig(config: SavedViewConfig) {
|
||||
this.filterRules = config.filterRules
|
||||
this.docs.setFilterRules(config.filterRules)
|
||||
this.docs.currentSortField = config.sortField
|
||||
this.docs.currentSortDirection = config.sortDirection
|
||||
this.reload()
|
||||
}
|
||||
|
||||
saveViewConfig() {
|
||||
let modal = this.modalService.open(SaveViewConfigDialogComponent, {backdrop: 'static'})
|
||||
modal.componentInstance.saveClicked.subscribe(formValue => {
|
||||
this.savedViewConfigService.saveConfig({
|
||||
filterRules: cloneFilterRules(this.filterRules),
|
||||
title: formValue.title,
|
||||
showInDashboard: formValue.showInDashboard,
|
||||
showInSideBar: formValue.showInSideBar,
|
||||
sortDirection: this.docs.currentSortDirection,
|
||||
sortField: this.docs.currentSortField
|
||||
})
|
||||
modal.close()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,17 @@
|
||||
<form [formGroup]="saveViewConfigForm" class="needs-validation" novalidate (ngSubmit)="save()">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title" id="modal-basic-title">Save current view</h4>
|
||||
<button type="button" class="close" aria-label="Close" (click)="cancel()">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<app-input-text title="Title" formControlName="title"></app-input-text>
|
||||
<app-input-check title="Show in side bar" formControlName="showInSideBar"></app-input-check>
|
||||
<app-input-check title="Show in dashboard" formControlName="showInDashboard"></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>
|
||||
</div>
|
||||
</form>
|
@@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { SaveViewConfigDialogComponent } from './save-view-config-dialog.component';
|
||||
|
||||
describe('SaveViewConfigDialogComponent', () => {
|
||||
let component: SaveViewConfigDialogComponent;
|
||||
let fixture: ComponentFixture<SaveViewConfigDialogComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ SaveViewConfigDialogComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(SaveViewConfigDialogComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@@ -0,0 +1,33 @@
|
||||
import { Component, EventEmitter, OnInit, Output } from '@angular/core';
|
||||
import { FormControl, FormGroup } from '@angular/forms';
|
||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
|
||||
@Component({
|
||||
selector: 'app-save-view-config-dialog',
|
||||
templateUrl: './save-view-config-dialog.component.html',
|
||||
styleUrls: ['./save-view-config-dialog.component.css']
|
||||
})
|
||||
export class SaveViewConfigDialogComponent implements OnInit {
|
||||
|
||||
constructor(private modal: NgbActiveModal) { }
|
||||
|
||||
@Output()
|
||||
public saveClicked = new EventEmitter()
|
||||
|
||||
saveViewConfigForm = new FormGroup({
|
||||
title: new FormControl(''),
|
||||
showInSideBar: new FormControl(false),
|
||||
showInDashboard: new FormControl(false),
|
||||
})
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
save() {
|
||||
this.saveClicked.emit(this.saveViewConfigForm.value)
|
||||
}
|
||||
|
||||
cancel() {
|
||||
this.modal.close()
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user